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惊群的代码后面有时间再进行补充。

posted @ 2020-06-22 03:47  sp0917  阅读(401)  评论(0编辑  收藏  举报