上一篇博客用多线程实现服务端和多个客户端的通信,但是在实际应用中如果服务端有高并发的需求,多线程并不是一个好选择。
实现高并发的一种方法是IO多路复用,也就是select,poll,epoll等等。
于是我采用epoll再修改了服务端,实现单线程服务多个客户端。
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> // open function
#include <unistd.h> // close function
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <sys/epoll.h>const int PORT = 8888;
/*listen_loop(): epoll监听套接字,作不同处理accept_conn(): 新的客户端连接进来,执行accept,将fd加入epoll setrecv_message(): recv并且重复输出一份给客户端*/
void listen_loop();
void accept_conn(unsigned int sock_fd, unsigned int epollfd);
void recv_message(unsigned int sock_fd);int main(void) {int sock_fd;struct sockaddr_in server_addr;//初始化socketsock_fd = socket(AF_INET, SOCK_STREAM, 0);if (sock_fd < 0) {perror("socket:");return 0;}//编辑地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;//ipv_4server_addr.sin_port = htons(PORT);//监听端口8888server_addr.sin_addr.s_addr = INADDR_ANY;//本地的任意地址//绑定然后监听if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("bind:");return 0;}if (listen(sock_fd, 10) < 0) {perror("listen");return 0;}listen_loop(sock_fd);return 0;
}
void accept_conn(unsigned int sock_fd, unsigned int epollfd) {struct sockaddr_in clientaddr;struct epoll_event event;socklen_t len = sizeof(struct sockaddr);int accept_fd = 0;accept_fd = accept(sock_fd, (struct sockaddr*)&clientaddr, &len);if (accept_fd <= 0) {perror("accept error");return;}//将新建连接加入epoll setevent.data.fd = accept_fd;event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;epoll_ctl(epollfd, EPOLL_CTL_ADD, accept_fd, &event);return;
}void recv_message(unsigned int sock_fd) {char recv_buf[1024], send_buf[1024];memset(recv_buf, 0, sizeof(recv_buf));memset(send_buf, 0, sizeof(send_buf));recv(sock_fd, recv_buf, sizeof(recv_buf), 0);fputs(recv_buf, stdout);strcpy(send_buf, recv_buf);send(sock_fd, send_buf, sizeof(send_buf), 0);return;
}
void listen_loop(unsigned int sock_fd)
{int epollfd, i, ret;int timeout = 300;struct epoll_event event;struct epoll_event eventList[10];/*创建epoll监听事件*/epollfd = epoll_create(10);event.events = EPOLLIN | EPOLLET;event.data.fd = sock_fd;/*注册epoll监听事件.*/if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &event) < 0) {printf("register epoll event err !");return;}while (1) {ret = epoll_wait(epollfd, eventList, 10, timeout);/*epoll事件错误.*/if (ret < 0) {printf("epoll event err!");break;}/*无事件返回.*/else if (ret == 0) {continue;}/*epoll返回事件.*/for (i = 0; i < ret; i++) {/*epoll 错误*/if ((eventList[i].events & EPOLLERR) || (eventList[i].events & EPOLLHUP) || !(eventList[i].events & EPOLLIN)) {printf("epoll error\n");close(eventList[i].data.fd);exit(-1);}//half connectionif (eventList[i].events & EPOLLRDHUP) {printf("//one client close the conne.//\n");close(eventList[i].data.fd);}/*accept事件*/if (eventList[i].data.fd == sock_fd) {accept_conn(sock_fd, epollfd);}/*非sock_fd则为其他事件.*/else {recv_message(eventList[i].data.fd);}}}close(epollfd);close(sock_fd);return;
}