Epoll实现服务端开发
1.Epoll事件的触发模式
1.Level Trigger没有处理反复发送(效率低,开发简单,select/epoll默认触发模式)
2.Edge Trigger只发送一次(效率高,开发困难)
2.Epoll重要的API
1.int epoll_create();
2.int epoll_ctl(epfd, op, fd, struct epoll_event* event);
3.int epoll_wait(epfd, events, maxevents, timeout);
3.Epoll的事件
1.EPOLLET 边缘触发
2.EPOLLIN 读事件
3.EPOLLOUT 写事件
4.EPOLLPRI 服务中断
5.EPOLLERR 读写出错
6.EPOLLHUP 服务挂起
4.epoll_ctl相关操作
1.EPOLL_CTL_ADD 添加文件描述符
2.EPOLL_CTL_MOD 修改已存在的描述符
3.EPOLL_CTL_DEL 删除已存在的描述符
5.Epoll重要结构体
1 typedef union epoll_data { 2 void* ptr; 3 int fd; 4 uint32_t u32; 5 uint64_t u64; 6 } epoll_data_t; 7 8 struct epoll_event { 9 uint32_t events; // epoll events 10 epoll_data_t data; // user data variable 11 };
6.epoll实现高性能网络服务器
1 /** 2 epoll.c 3 */ 4 #include <stdio.h> 5 #include <errno.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <string.h> 9 #include <strings.h> 10 #include <sys/types.h> 11 #include <sys/socket.h> 12 #include <netinet/in.h> 13 #include <sys/epoll.h> 14 15 #define PORT 8111 16 #define MSG_LEN 1024 17 #define MAX_EVENTS 20 18 #define TIMEOUT 500 19 20 int main(int argc, char* argv[]) 21 { 22 int ret = -1; 23 int on = 1; 24 int backlog = 10; 25 int flags = 1; 26 int socket_fd = -1; 27 int epoll_fd = -1;; 28 int event_num = 0; 29 struct epoll_event ev, events[MAX_EVENTS]; 30 struct sockaddr_in local_addr, remote_addr; 31 char buff[MSG_LEN] = { 0 }; 32 33 socket_fd = socket(AF_INET, SOCK_STREAM, 0); 34 if (socket_fd < 0) { 35 printf("create socket fd failed.\n"); 36 _exit(-1); 37 } 38 39 flags = fcntl(socket_fd, F_GETFL, 0); 40 fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK); 41 42 ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 43 if (ret < 0) { 44 printf("set socket option failed.\n"); 45 } 46 47 bzero(&local_addr, sizeof(local_addr)); 48 local_addr.sin_family = AF_INET; 49 local_addr.sin_port = htons(PORT); 50 local_addr.sin_addr.s_addr = htonl(INADDR_ANY); 51 ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)); 52 if (ret < 0) { 53 printf("bind port[%d] failed.\n", PORT); 54 _exit(-1); 55 } 56 57 ret = listen(socket_fd, backlog); 58 if (ret < 0) { 59 printf("listen socket[%d] port[%d] failed.\n", socket_fd, PORT); 60 _exit(-1); 61 } 62 printf("listening on port[%d]...\n", PORT); 63 64 epoll_fd = epoll_create(256); 65 ev.events = EPOLLIN; 66 ev.data.fd = socket_fd; 67 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &ev); 68 69 while (1) { 70 event_num = epoll_wait(epoll_fd, events, MAX_EVENTS, TIMEOUT); 71 for (int i = 0; i < event_num; ++i) 72 { 73 if (events[i].data.fd == socket_fd) { 74 socklen_t addr_len = sizeof(struct sockaddr); 75 int accept_fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len); 76 flags = fcntl(accept_fd, F_GETFL, 0); 77 fcntl(accept_fd, F_SETFL, flags | O_NONBLOCK); 78 ev.events = EPOLLIN | EPOLLET; 79 ev.data.fd = accept_fd; 80 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, accept_fd, &ev); 81 printf("connection[%d] estasblished...\n", accept_fd); 82 } else if (events[i].events & EPOLLIN) { 83 memset(buff, 0, sizeof(buff)); 84 ret = recv(events[i].data.fd, (void *)buff, sizeof(buff), 0); 85 86 if (ret <= 0) { 87 switch (errno) { 88 case EAGAIN: 89 printf("receive EAGAIN, break...\n"); 90 break; 91 case EINTR: 92 do { 93 printf("index[%d] fd[%d] receive EINTR, rereceive...\n", i, events[i].data.fd); 94 memset(buff, 0, sizeof(buff)); 95 ret = recv(events[i].data.fd, (void *)buff, sizeof(buff), 0); 96 } while (ret < 0 && errno == EINTR); 97 break; 98 default: 99 printf("receive ret[%d] fd[%d] errno[%d|%s]\n", ret, events[i].data.fd, errno, strerror(errno)); 100 close(events[i].data.fd); 101 ev.events = EPOLLIN | EPOLLET; 102 ev.data.fd = events[i].data.fd; 103 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &ev); 104 break; 105 } 106 } 107 108 if (ret > 0) { 109 if (ret == MSG_LEN){ 110 printf("maybe more data...\n"); 111 } 112 113 buff[ret] = '\0'; 114 printf("recv[%s]\n", buff); 115 send(events[i].data.fd, (void *)buff, strlen(buff), 0); 116 } 117 } 118 } 119 } 120 121 close(socket_fd); 122 123 return 0; 124 } 125 126 # gcc -std=c11 -g -o epoll epoll.c 127 # ./epoll
7.epoll + fork
1.在服务开始的时候创建进程池,在运行时创建会很耗时
2.进程/线程可以与CPU进行绑定,使得CPU主核处理更多的工作
3.会出现惊群现象,将监听任务放在主进程中,子进程负责数据的收发
4.或者在某一时刻只有一个进程执行监听任务
5.使用互斥量;
1 /** 2 epoll_fork.c 3 */ 4 #include <stdio.h> 5 #include <errno.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <string.h> 9 #include <strings.h> 10 #include <sys/types.h> 11 #include <sys/wait.h> 12 #include <sys/socket.h> 13 #include <netinet/in.h> 14 #include <sys/epoll.h> 15 16 #define PORT 8111 17 #define MSG_LEN 1024 18 #define MAX_EVENTS 20 19 #define TIMEOUT 500 20 #define MAX_PROCESS 5 21 22 int main(int argc, char* argv[]) 23 { 24 int ret = -1; 25 int on = 1; 26 int backlog = 10; 27 int flags = 1; 28 pid_t pid = -1; 29 int socket_fd = -1; 30 int epoll_fd = -1;; 31 int event_num = 0; 32 struct epoll_event ev, events[MAX_EVENTS]; 33 struct sockaddr_in local_addr, remote_addr; 34 char buff[MSG_LEN] = { 0 }; 35 36 socket_fd = socket(AF_INET, SOCK_STREAM, 0); 37 if (socket_fd < 0) { 38 printf("create socket fd failed.\n"); 39 _exit(-1); 40 } 41 42 flags = fcntl(socket_fd, F_GETFL, 0); 43 fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK); 44 45 ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 46 if (ret < 0) { 47 printf("set socket option failed.\n"); 48 } 49 50 bzero(&local_addr, sizeof(local_addr)); 51 local_addr.sin_family = AF_INET; 52 local_addr.sin_port = htons(PORT); 53 local_addr.sin_addr.s_addr = htonl(INADDR_ANY); 54 ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)); 55 if (ret < 0) { 56 printf("bind port[%d] failed.\n", PORT); 57 _exit(-1); 58 } 59 60 ret = listen(socket_fd, backlog); 61 if (ret < 0) { 62 printf("listen socket[%d] port[%d] failed.\n", socket_fd, PORT); 63 _exit(-1); 64 } 65 printf("listening on port[%d]...\n", PORT); 66 67 68 69 for (int i = 0; i < MAX_PROCESS; ++i) 70 { 71 // father process 72 if (pid != 0) { 73 pid = fork(); 74 } 75 } 76 77 if (pid == 0) { 78 epoll_fd = epoll_create(256); 79 ev.events = EPOLLIN; 80 ev.data.fd = socket_fd; 81 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &ev); 82 83 while (1) { 84 event_num = epoll_wait(epoll_fd, events, MAX_EVENTS, TIMEOUT); 85 for (int i = 0; i < event_num; ++i) 86 { 87 if (events[i].data.fd == socket_fd) { 88 printf("socket fd[%d] coming...\n", socket_fd); 89 socklen_t addr_len = sizeof(struct sockaddr); 90 int accept_fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len); 91 flags = fcntl(accept_fd, F_GETFL, 0); 92 fcntl(accept_fd, F_SETFL, flags | O_NONBLOCK); 93 ev.events = EPOLLIN | EPOLLET; 94 ev.data.fd = accept_fd; 95 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, accept_fd, &ev); 96 printf("connection[%d] estasblished...\n", accept_fd); 97 } else if (events[i].events & EPOLLIN) { 98 memset(buff, 0, sizeof(buff)); 99 ret = recv(events[i].data.fd, (void *)buff, sizeof(buff), 0); 100 101 if (ret <= 0) { 102 switch (errno) { 103 case EAGAIN: 104 printf("receive EAGAIN, break...\n"); 105 break; 106 case EINTR: 107 do { 108 printf("index[%d] fd[%d] receive EINTR, rereceive...\n", i, events[i].data.fd); 109 memset(buff, 0, sizeof(buff)); 110 ret = recv(events[i].data.fd, (void *)buff, sizeof(buff), 0); 111 } while (ret < 0 && errno == EINTR); 112 break; 113 default: 114 printf("receive ret[%d] fd[%d] errno[%d|%s]\n", ret, events[i].data.fd, errno, strerror(errno)); 115 close(events[i].data.fd); 116 ev.events = EPOLLIN | EPOLLET; 117 ev.data.fd = events[i].data.fd; 118 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &ev); 119 break; 120 } 121 } 122 123 if (ret > 0) { 124 if (ret == MSG_LEN){ 125 printf("maybe more data...\n"); 126 } 127 128 buff[ret] = '\0'; 129 printf("recv[%s]\n", buff); 130 send(events[i].data.fd, (void *)buff, strlen(buff), 0); 131 } 132 } 133 } 134 } 135 } else { 136 do { 137 pid = waitpid(-1, NULL, 0); 138 printf("child pid[%d]\n", pid); 139 } while (pid != -1); 140 close(socket_fd); 141 } 142 143 return 0; 144 } 145 146 # gcc -std=c11 -g -o epoll_fork epoll_fork.c 147 # ./epoll_fork
关于绑定CPU核epoll惊群的代码后面有时间再进行补充。