server.c
// gcc -lpthread server.c -o server #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <pthread.h> //#endif #define BUF_SIZE 1024 //默认缓冲区 #define SERVER_PORT 11111 //监听端口 #define SERVER_HOST "127.0.0.1" //服务器IP地址 #define EPOLL_RUN_TIMEOUT -1 //epoll的超时时间 #define EPOLL_SIZE 10000 //epoll监听的客户端的最大数目 #define LISTEN_SIZE 10 //监听队列长度 #define STR_WELCOME "Welcome to seChat! You ID is : Client #%d" #define STR_MESSAGE "Client #%d>> %s" #define STR_NOONE_CONNECTED "Noone connected to server expect you!" #define CMD_EXIT "EXIT" //两个有用的宏定义:检查和赋值并且检测 #define CHK(eval) if(eval < 0){perror("eval"); exit(-1);} #define CHK2(res, eval) if((res = eval) < 0){perror("eval"); exit(-1);} void* handle_message(void *arg); int main(int argc, char *argv[]) { int listener; //监听socket struct sockaddr_in addr, peer; addr.sin_family = PF_INET; addr.sin_port = htons(SERVER_PORT); addr.sin_addr.s_addr = inet_addr(SERVER_HOST); socklen_t socklen; socklen = sizeof(struct sockaddr_in); int client; CHK2(listener, socket(PF_INET, SOCK_STREAM, 0)); //初始化监听socket // 设置套接字选项避免地址使用错误 int on=1; if((setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0) { perror("setsockopt failed"); exit(EXIT_FAILURE); } CHK(bind(listener, (struct sockaddr *)&addr, sizeof(addr))); //绑定监听socket //printf("listen\n"); CHK(listen(listener, LISTEN_SIZE)); //设置监听 printf("listening\n"); while (1) { //printf("accept\n"); CHK2(client, accept(listener, (struct sockaddr *)&peer, &socklen)); printf("accepted\n"); pthread_t reader; int rt = pthread_create(&reader, NULL, handle_message, (void *)&client); if (-1 == rt) { printf("thread creation error\n"); return -1; } } } void* handle_message(void *arg) { int client = *((int*)arg); //sleep(5); char buf[BUF_SIZE]; int len; while(1){ bzero(buf, BUF_SIZE); CHK2(len, recv(client, buf, BUF_SIZE, 0)); //接受客户端信息 if (len == 0) //客户端关闭或出错,关闭socket,并从list移除socket { printf("close-client: %d\n", client); CHK(close(client)); return NULL; } else //向客户端发送信息 { CHK(send(client, STR_NOONE_CONNECTED, strlen(STR_NOONE_CONNECTED), 0)); printf("receive: %s from %d\n", buf, client); } } //return len; //return NULL; }
// gcc client.c -o client // gcc -lpthread client.c -o client #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/epoll.h> //#endif #define BUF_SIZE 1024 //默认缓冲区 #define SERVER_PORT 11111 //监听端口 #define SERVER_HOST "127.0.0.1" //服务器IP地址 #define EPOLL_RUN_TIMEOUT -1 //epoll的超时时间 #define EPOLL_SIZE 10000 //epoll监听的客户端的最大数目 #define STR_WELCOME "Welcome to seChat! You ID is : Client #%d" #define STR_MESSAGE "Client #%d>> %s" #define STR_NOONE_CONNECTED "Noone connected to server expect you!" #define CMD_EXIT "EXIT" //两个有用的宏定义:检查和赋值并且检测 #define CHK(eval) if(eval < 0){perror("eval"); exit(-1);} #define CHK2(res, eval) if((res = eval) < 0){perror("eval"); exit(-1);} char message[BUF_SIZE]; /* 流程: 调用fork产生两个进程,两个进程通过管道进行通信 子进程:等待客户输入,并将客户输入的信息通过管道写给父进程 父进程:接受服务器的信息并显示,将从子进程接受到的信息发送给服务器 */ int main(int argc, char *argv[]) { int sock, pid, pipe_fd[2], epfd; struct sockaddr_in addr; addr.sin_family = PF_INET; addr.sin_port = htons(SERVER_PORT); addr.sin_addr.s_addr = inet_addr(SERVER_HOST); static struct epoll_event ev, events[2]; ev.events = EPOLLIN | EPOLLET; //退出标志 int continue_to_work = 1; CHK2(sock, socket(PF_INET, SOCK_STREAM, 0)); CHK(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0); CHK(pipe(pipe_fd)); CHK2(epfd, epoll_create(EPOLL_SIZE)); ev.data.fd = sock; CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev)); ev.data.fd = pipe_fd[0]; CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, pipe_fd[0], &ev)); // 调用fork产生两个进程 CHK2(pid, fork()); switch (pid) { case 0: // 子进程 close(pipe_fd[0]); // 关闭读端 printf("Enter 'exit' to exit\n"); while (continue_to_work) { bzero(&message, BUF_SIZE); fgets(message, BUF_SIZE, stdin); // 当收到exit命令时,退出 if (strncasecmp(message, CMD_EXIT, strlen(CMD_EXIT)) == 0) { continue_to_work = 0; } else { CHK(write(pipe_fd[1], message, strlen(message) - 1)); } } break; default: // 父进程 close(pipe_fd[1]); // 关闭写端 int epoll_events_count, res; while (continue_to_work) { CHK2(epoll_events_count, epoll_wait(epfd, events, 2, EPOLL_RUN_TIMEOUT)); for (int i = 0; i < epoll_events_count; i++) { bzero(&message, BUF_SIZE); if (events[i].data.fd == sock) //从服务器接受信息 { CHK2(res, recv(sock, message, BUF_SIZE, 0)); if (res == 0) //服务器已关闭 { CHK(close(sock)); continue_to_work = 0; } else { printf("%s\n", message); } } else //从子进程接受信息 { CHK2(res, read(events[i].data.fd, message, BUF_SIZE)); if (res == 0) { continue_to_work = 0; } else { CHK(send(sock, message, BUF_SIZE, 0)); } } } } } if (pid) { close(pipe_fd[0]); close(sock); } else { close(pipe_fd[1]); } return 0; }