linux高性能服务器编程之epoll

简介:

一.概述:

epoll是多路复用的一种,但它比select和poll更加高效。具体体现在以下几个方面:

(1).select能打开的文件描述符是有一定限制的,默认情况下是2048,这对应那些大型服务器来说h是不足的。但 epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat  /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

(2).因为文件描述符是内核管理的,所以每次调用select或poll,都需要把fd集合从用户态拷贝到内核态,并且每次检查文件描述符的状态时,都要在内核遍历所有文件描述符,这个开销在fd很多时会很大。而epoll采用了nmap(内存映射)(和共享内存一样),内核和用户空间共用一行份fd集。

(3).另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。



其它优化:

(4).epoll会给所有要关注的文件描述符建立一个红黑树,这样在查找某一个文件描述符时效率会有所提升。

(5).epoll会给准备好的文件描述符建立一个链表,这样查找一个已准备好的文件描述符时就不用在以前所有要关注的fd集中查找了。




二.epoll用法篇:

epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel2.5.44),它几乎具备了之前所说select和poll的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll有epoll_create,epoll_ctl,epoll_wait 3个系统调用。



(1).epoll_create:

1
int    epoll_create( int  size);

创建一个epoll的句柄(后面会根据这个句柄创建红黑树)。自从linux2.6.8之后,size参数是被忽略的(也就是说,可以为任意值)。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

返回值:成功返回一个epoll句柄,失败返回-1;


(2).epoll_ctl

1
int  epoll_ctl( int  epfd,  int  op,  int  fd,  struct  epoll_event *event);

函数描述:epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。(一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。)

返回值:成功返回0,失败返回-1;

epfd参数:epoll_create创建的一个epoll句柄。

op参数:表示要执行的动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

fd参数:需要监听的文件描述符。

event参数:告诉内核需要监听什么事。struct epoll_event结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
  The event argument describes the object linked to the file descriptor fd.  The  struct  epoll_event is defined as :
 
            typedef  union  epoll_data {
                void         *ptr;
                int           fd;
                uint32_t     u32;
                uint64_t     u64;
            } epoll_data_t;
 
            struct  epoll_event {
                uint32_t     events;       /* Epoll events */
                epoll_data_t data;         /* User data variable */
            };

events有如下值:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水水平触发(Level
Triggered)来说的。(epoll默认为水平触发)

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。



(3).epoll_wait:

1
int  epoll_wait( int  epfd,  struct  epoll_event * events,  int  maxevents,  int  timeout);

函数功能:监听在epoll监控的事件中已经发送的事件。

返回值:成功返回监听文件描述符集中已经准备好的文件描述符,返回0代表timeout,失败返回-1。

epoll参数:epoll_create创建的epoll句柄。

events参数:输出型参数,保存监听文件描述符集中已经准备好的文件描述符集。

maxevents参数:events数组的大小。

timeout参数:超时时间。单位为毫秒。




三.LT模式下的阻塞模式。

相关代码:

server.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  /****************************************                                                                                                 
   2     > File Name:epoll_server.c
   3     > Author:xiaoxiaohui
   4     > mail:1924224891@qq.com
   5     > Created Time:2016年05月28日 星期六 15时38分17秒
   6 ****************************************/
  
   8 #include<stdio.h>
   9 #include<stdlib.h>
  10 #include<sys/types.h>
  11 #include<sys/socket.h>
  12 #include<arpa/inet.h>
  13 #include<netinet/in.h>
  14 #include<string.h>
  15 #include<unistd.h>
  16 #include<sys/epoll.h>
  17 
  18 #define LEN 1024地
  19  const  char * IP =  "127.0.0.1" ;
  20  const  int  PORT = 8080;
  21  const  int  BACKLOG = 5;
  22  int  timeout = 5000;
  23  const  int  MAXEVENTS = 64;
  24  struct  sockaddr_in local;
  25  struct  sockaddr_in client;              
 
  26  int  SIZE_CLIENT =  sizeof (client);
  27 
  28  typedef  struct  data_buf      //用于存储epoll_event中的data中的不同元素
  29 {
  30      int  fd;
  31      char  buf[LEN];
  32 }data_buf_t, *data_buf_p;地
  33 
  34 
  35  int  ListenSock()
  36 {
  37      int  listenSock = socket(AF_INET, SOCK_STREAM, 0);
  38      if (listenSock < 0)
  39     {
  40          perror ( "socket" );
  41          exit (1);
  42     }
  43 
  44     local.sin_family = AF_INET;
  45     local.sin_port = htons(PORT);
  46     local.sin_addr.s_addr = inet_addr(IP);
  47      if ( bind(listenSock, ( struct  sockaddr*)&local,  sizeof (local)) < 0)
  48     {
  49          perror ( "bind" );
  50          exit (2);
  51     }
  52 
  53      if ( listen(listenSock, BACKLOG) < 0)
  54     {
  55          perror ( "listen" );
  56          exit (3);
  57     }
  58 
  59      return  listenSock;
  60 }
  61 
  62  static  int  epoll_fd( int  listenSock)
  63 {
  64 
  65      int  epoll_fd = epoll_create(256);            //size随便选一个值
  66 
  67      struct  epoll_event ev;           //把listenSock设置进epoll_fd中
  68     ev.events = EPOLLIN;
  69     ev.data.fd = listenSock;
  70     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenSock, &ev);         //系统会维护一个红黑树
  71 
  72      struct  epoll_event ev_outs[MAXEVENTS];       //准备好的队列
  73      int  max = MAXEVENTS;
  74     
  75      while (1)                                                                                                                              
  76     {
  77          int  num = -1;
  78          switch ( num = epoll_wait(epoll_fd, ev_outs, max, timeout))
  79         {
  80              case  0:      //timeout                                                                                                         
  81                  printf ( "timeout.....\n" );
  82                  break ;
  83              case  -1:     //error
  84                  perror ( "epoll_wait" );
  85                  break ;
  86              default :
  87                  for ( int  index = 0; index < num; index++)   
  88                 {
  89                      if (ev_outs[index].data.fd == listenSock && (ev_outs[index].events & EPOLLIN))       //监听套接字准备就绪
  90                     {
  91                          printf ( "accept is ready\n" );
  92                          int  linkSock = accept(listenSock, (s地truct sockaddr*)&client, &SIZE_CLIENT);
  93                          if (linkSock < 0)
  94                         {
  95                              perror ( "accept" );
  96                              continue ;            //这次可能是一个新客户端的请求,所以后面可能还有文件描述符准备就绪了
  97                         }地
  98 
  99                         ev.events = EPOLLIN;      //把新套接字放到红黑树中
100                         ev.data.fd = linkSock;
101                         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, linkSock, &ev);
102                     }
103                      else          //已链接套接字准备就绪
104                     {
105                          if (ev_outs[index].events & EPOLLIN)       //读事件准备就绪
106                         {
107                             data_buf_p mem = (data_buf_p) malloc ( sizeof (data_buf_t));
108                              memset (mem->buf,  '\0' sizeof (mem->buf));
109                             mem->fd = ev_outs[index].data.fd;
110 
111                              int  ret = read(mem->fd, mem->buf,  sizeof (mem->buf));
112                              if (ret > 0)
113                             {
114                                 mem->buf[ret] =  '\0' ;
115                                  printf ( "client# %s\n" , mem->buf);
116                                                                                                                                           
117                                 ev.data.ptr = mem;          //mem中即保持了fd,又保持了buf数据
118                                 ev.events = EPOLLOUT;     //读事件已经完成,现在要关心写事件
119                                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, mem->fd, &ev);
120                             }
121                              else  if (ret == 0 )       //客户端已关闭
122                             {
123                                  printf ( "client is closed\n" );
124                                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ev_outs[index].data.fd, NULL);      //把该文件描述符从红黑树中移除
125                                 close(ev_outs[index].data.fd);
126                                  free (mem);
127                             }
128                              else
129                             {
130                                  perror ( "read" );
131                                  continue ;
132                             }
133                         }
134                          else  if (ev_outs[index].events & EPOLLOUT)    //写事件准备就绪
135                         {
136                             data_buf_p mem = (data_buf_p)ev_outs[index].data.ptr;
137                              int  fd = mem->fd;
138                              char * buf = mem->buf;
139 
140                              if ( write(fd, buf,  strlen (buf)) < 0)
141                             {
142                                  perror ( "write" );
143                                  continue ;
144                             }
145 
146                             ev.events = EPOLLIN;        //这个文件描述符的写事件已完成,下次关心读事件
147                             ev.data.fd = mem->fd;
148                             epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
149                         }
150                          else    //DoNothing
151                         {}
152                     }
153                 }
154                  break ;
155         }
156     }
157 }
158 
159  int  main()
160 {
161      int  listen地Sock = ListenSock();
162     epoll_fd(listenSock);
163     close(listenSock);
164      return  0;
165 }


client.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/****************************************                                                                                                 
   2     > File Name:client.c
   3     > Author:xiaoxiaohui
   4     > mail:1924224891@qq.com
   5     > Created Time:2016年05月23日 星期一 12时30分01秒
   6 ****************************************/
  
   8 #include<stdio.h>
   9 #include<stdlib.h>
  10 #include<string.h>
  11 #include<sys/types.h>
  12 #include<sys/socket.h>
  13 #include<netinet/in.h>
  14 #include<arpa/inet.h>
  15 #include<sys/ time .h>
  16 #include<unistd.h>
  17 
  18 #define LEN 1024
  19  const  int  PORT = 8080;
  20  const  char * IP =  "127.0.0.1" ;
  21  struct  sockaddr_in server;
  22  int  clientSock;
  23  char  buf[LEN];
  24 
  25  int  main()
  26 {
  27     clientSock = socket(AF_INET, SOCK_STREAM, 0);
  28      if (clientSock < 0)
  29     {
  30          perror ( "socket" );
  31          exit (1);
  32     }
  33 
  34     server.sin_family = AF_INET;
  35     server.sin_addr.s_addr = inet_addr(IP);
  36     server.sin_port = htons(PORT);
  37 
  38      if  ( connect(clientSock, ( struct  sockaddr*)&server,  sizeof (server)) < 0)
  39     {
  40          perror ( "connect" );
  41          exit (2);
  42     }
  43 
  44      while (1)
  45     {
  46          memset (buf,  '\0' , LEN);
  47          printf ( "please input: " );
  48          gets (buf);
  49         write(clientSock, buf,  strlen (buf));
  50 
  51          memset (buf,  '地\0' , LEN);
  52          int  ret = read(clientSock, buf, LEN);
  53         buf[ret] =  '\0' ;
  54          printf ( "echo: %s\n" , buf);  
  55     }
  56 
  57      return  0;
  58 }

执行结果:

wKioL1dMUMSwl4yxAABBcUtFoTY230.png-wh_50


wKiom1dMT96D9SFfAABDh8Gm5KQ549.png-wh_50




四.ET模式下的非阻塞模式:

(1).概述:

ET模式要在epoll_ctl中进行设置(具体设置看代码),并且要把套接字设置为非阻塞模式。

下面概况以下ET与LT的区别:

ET (edge-triggered)是高效的工作方式,只支持no-block socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式下,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。
因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。


所以在下面代码中要封装一个read_data函数,来确保一次把数据缓冲区内的数据读取完。因为ET模式下只支持非阻塞模式,所以还要把每个套接字设置为非阻塞的。


下面代码实现      在浏览器访问服务器程序,并在浏览器中打印hello world :)         ,当服务器程序发送完给浏览器的数据时,服务器程序关闭链接。

相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/****************************************                                                                                                 
   2     > File Name:epoll_server.c
   3     > Author:xiaoxiaohui
   4     > mail:1924224891@qq.com
   5     > Created Time:2016年05月28日 星期六 15时38分17秒
   6 ****************************************/
  
   8 #include<stdio.h>
   9 #include<stdlib.h>
  10 #include<sys/types.h>
  11 #include<sys/socket.h>
  12 #include<arpa/inet.h>
  13 #include<netinet/in.h>
  14 #include<string.h>
  15 #include<unistd.h>
  16 #include<sys/epoll.h>
  17 #include<fcntl.h>
  18 #include< errno .h>
  19 
  20 #define LEN 1024
  21  const  char * IP =  "127.0.0.1" ;
  22  const  int  PORT = 8080;
  23  const  int  BACKLOG = 5;
  24  int  timeout = 5000;
  25  const  int  MAXEVENTS = 64;
  26  struct  sockaddr_in local;
  27  struct  sockaddr_in client;
  28  int  SIZE_CLIENT =  sizeof (client);
  29 
  30  typedef  struct  data_buf      //用于存储epoll_event中的data中的不同元素
  31 {
  32      int  fd;
  33      char  buf[LEN];
  34 }data_buf_t, *data_buf_p;
  35 
  36  static  int  set_no_block( int  fd)        //把fd设置为非阻塞
  37 {
  38      int  oldfd = fcntl(fd, F_GETFL);
  39      if (oldfd < 0)
  40     {
41          perror ( "fcntl" );
  42          return  -1;
  43     }
  44 
  45      if ( fcntl(fd, F_SETFL, oldfd | O_NONBLOCK))
  46     {
  47          perror ( "fcntl" );
  48          return  -1;
  49     }
  50      return  0;
  51 }
  52 
  53  int  read_data( int  fd,  char * buf,  int  len)       //ET模式下读取数据,因为ET模式下只通知一次,所以要保证把所有数据都读完
  54 {                                               //成功返回读取的个数,失败返回-1,返回0代表读到文件尾
  55      int  index = 0;
  56      int  ret = -1;
  57 
  58      while (index < len)
  59     {
  60         ret = read(fd, buf + index, len - index);
  61          printf ( "the read return ret is %d\n" , ret);
  62          if (ret > 0)
  63         {
  64             index += ret;
  65         }
  66          else  if (ret < 0)
  67         {
  68              printf ( "the errno is %d\n" , errno );
  69              if ( errno  == EAGAIN)
  70             {
  71                  break ;
  72             }
  73         }
  74          else
  75         {                                                                                                                                 
  76              return  0;
  77         }
  78     }
  79 
  80      return  index;
  81 }                                                                                                                                         
  82 
  83 
  84 
  85 
  86  static  int  ListenSock()
  87 {
  88      int  listenSock = socket(AF_INET, SOCK_STREAM, 0);
  89      if (listenSock < 0)
  90     {
  91          perror ( "socket" );
  92          exit (1);
  93     }
  94 
  95      int  opt = 1;
  96      if ( setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &opt,  sizeof (opt)))      //设置端口复用   
  97     {
  98          perror ( "sersockopt" );
  99          exit (2);
100     }
101      if ( set_no_block(listenSock) != 0)       //设置为非阻塞    .............修改
102     {
103          printf ( "set_non_block is error\n" );
104          exit (3);
105     }
106 
107     local.sin_family = AF_INET;
108     local.sin_port = htons(PORT);
109     local.sin_addr.s_addr = inet_addr(IP);
110      if ( bind(listenSock, ( struct  sockaddr*)&local,  sizeof (local)) < 0)
111     {
112          perror ( "bind" );
113          exit (4);
114     }
115 
116      if ( listen(listenSock, BACKLOG) < 0)
117     {
118          perror ( "listen" );
119          exit (5);
120     }
121                                                                                                                                           
122      return  listenSock;
123 }
124 
125  static  int  epoll_fd( int  listenSock)
126 {
127 
128      int  epoll_fd = epoll_create(256);            //size随便选一个值
129 
130      struct  epoll_event ev;           //把listenSock设置进epoll_fd中
131     ev.events = EPOLLIN | EPOLLET;     //....................修改
132     ev.data.fd = listenSock;
133     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenSock, &ev);         //系统会维护一个红黑树
134 
135      struct  epoll_event ev_outs[MAXEVENTS];       //准备好的队列
136      int  max = MAXEVENTS;
137     
138      while (1)
139     {
140          int  num = -1;
141          switch ( num = epoll_wait(epoll_fd, ev_outs, max, timeout))
142         {
143              case  0:      //timeout
144                  printf ( "timeout.....\n" );
145                  break ;
146              case  -1:     //error
147                  perror ( "epoll_wait" );
148                  break ;
149              default :
150                  for ( int  index = 0; index < num; index++)   
151                 {
152                      if (ev_outs[index].data.fd == listenSock && (ev_outs[index].events & EPOLLIN))       //监听套接字准备就绪
153                     {
154                          printf ( "accept is ready\n" );
155                          int  linkSock = accept(listenSock, ( struct  sockaddr*)&client, &SIZE_CLIENT);
156                          if (linkSock < 0)
157                         {
158                              perror ( "accept" );
159                              continue ;            //这次可能是一个新客户端的请求,所以后面可能还有文件描述符准备就绪了
160                         }
161                                                                                                                                           
162                          if ( set_no_block(linkSock) != 0)       //设置为非阻塞    .............修改
163                         {
164                              printf ( "set_non_block is error\n" );
165                              exit (3);
166                         }
167 
168                         ev.events = EPOLLIN | EPOLLET;      //把新套接字放到红黑树中   ...................修改
169                         ev.data.fd = linkSock;
170                         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, linkSock, &ev);
171                     }
172                      else          //已链接套接字准备就绪
173                     {
174                          if (ev_outs[index].events & EPOLLIN)       //读事件准备就绪
175                         {
176                             data_buf_p mem = (data_buf_p) malloc ( sizeof (data_buf_t));
177                              memset (mem->buf,  '\0' sizeof (mem->buf));
178                             mem->fd = ev_outs[index].data.fd;
179 
180                              printf ( "read is ready\n" );
181                              int  ret = read_data(mem->fd, mem->buf,  sizeof (mem->buf));  //.........................修改
182                              printf ( "read is over, the ret is %d\n" , ret);
183                              if (ret > 0)
184                             {
185                                 mem->buf[ret] =  '\0' ;
186                                  printf ( "client# %s\n" , mem->buf);
187 
188                                 ev.data.ptr = mem;          //mem中即保持了fd,又保持了buf数据
189                                 ev.events = EPOLLOUT;     //读事件已经完成,现在要关心写事件
190                                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, mem->fd, &ev);
191                             }
192                              else  if (ret == 0 )       //客户端已关闭
193                             {
194                                  printf ( "client is closed\n" );
195                                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ev_outs[index].data.fd, NULL);      //把该文件描述符从红黑树中移除
196                                 close(ev_outs[index].data.fd);
197                                  free (mem);
198                             }
199                              else
200                             {
201                                  perror ( "read" );                                                                                           
202                                  continue ;
203                             }
204                         }
205                          else  if (ev_outs[index].events & EPOLLOUT)    //写事件准备就绪
206                         {
207                             data_buf_p mem = (data_buf_p)ev_outs[index].data.ptr;
208                              int  fd = mem->fd;
209                              char * buf = mem->buf;
210 
211                              char  *msg =  "HTTP/1.0 200 OK\r\n\r\nhello world:)\r\n" //.....................修改
212                              if ( write(fd, msg,  strlen (msg)) < 0)                 //.........................修改
213                             {
214                                  perror ( "write" );
215                                  continue ;
216                             }
217 
218                             epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);      //把该文件描述符从红黑树中移除    ............修改
219                             close(fd);                                        //.............................................修改
220                              free (mem);                                        //.............................................修改
221                             mem = NULL;
222 
223                  //          ev.events = EPOLLIN;       //这个文件描述符的写事件已完成,下次关心读事件  ..................修改
224                  //          ev.data.fd = mem->fd;     //........................................修改
225                  //          epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);  //....................修改
226                         }
227                          else    //DoNothing
228                         {}
229                     }
230                 }
231                  break ;
232         }
233     }
234 }
235 
236  int  main()
237 {
238      int  listenSock = ListenSock();
239     epoll_fd(listenSock);
240     close(listenSock);
241      return  0;
242 }


执行结果:

wKioL1dMVvPCBL2eAAAWGGzTWss933.png-wh_50










本文转自 ye小灰灰  51CTO博客,原文链接:http://blog.51cto.com/10704527/1784649,如需转载请自行联系原作者
目录
相关文章
|
7月前
|
弹性计算 安全 Linux
阿里云服务器ECS安装宝塔Linux面板、安装网站(新手图文教程)
本教程详解如何在阿里云服务器上安装宝塔Linux面板,涵盖ECS服务器手动安装步骤,包括系统准备、远程连接、安装命令执行、端口开放及LNMP环境部署,手把手引导用户快速搭建网站环境。
|
9月前
|
Linux 网络安全 数据安全/隐私保护
使用Linux系统的mount命令挂载远程服务器的文件夹。
如此一来,你就完成了一次从你的Linux发车站到远程服务器文件夹的有趣旅行。在这个技术之旅中,你既探索了新地方,也学到了如何桥接不同系统之间的距离。
1577 21
|
8月前
|
Java Linux 网络安全
Linux云端服务器上部署Spring Boot应用的教程。
此流程涉及Linux命令行操作、系统服务管理及网络安全知识,需要管理员权限以进行配置和服务管理。务必在一个测试环境中验证所有步骤,确保一切配置正确无误后,再将应用部署到生产环境中。也可以使用如Ansible、Chef等配置管理工具来自动化部署过程,提升效率和可靠性。
805 13
|
8月前
|
安全
基于Reactor模式的高性能服务器之Acceptor组件(处理连接)
本节介绍了对底层 Socket 进行封装的设计与实现,通过 `Socket` 类隐藏系统调用细节,提供简洁、安全、可读性强的接口。重点包括 `Socket` 类的核心作用(管理 `sockfd_`)、成员函数的功能(如绑定地址、监听、接受连接等),以及 `Acceptor` 组件的职责:监听连接、接收新客户端连接并分发给上层处理。同时说明了 `Acceptor` 与 `EventLoop` 和 `TcpServer` 的协作关系,并展示了其成员变量和关键函数的工作机制。
189 2
|
8月前
|
监控 Linux 网络安全
FinalShell SSH工具下载,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux
FinalShell是一款国人开发的多平台SSH客户端工具,支持Windows、Mac OS X和Linux系统。它提供一体化服务器管理功能,支持shell和sftp同屏显示,命令自动提示,操作便捷。软件还具备加速功能,提升访问服务器速度,适合普通用户和专业人士使用。
2756 0
|
10月前
|
Ubuntu Linux 网络安全
在Linux云服务器上限制特定IP进行SSH远程连接的设置
温馨提示,修改iptables规则时要格外小心,否则可能导致无法远程访问你的服务器。最好在掌握足够技术知识和理解清楚操作含义之后再进行。另外,在已经配置了防火墙的情况下,例如ufw(Ubuntu Firewall)或firewalld,需要按照相应的防火墙的规则来设置。
529 24
|
8月前
|
存储 安全 Linux
Linux服务器上安装配置GitLab的步骤。
按照以上步骤,一个基础的GitLab服务应该运行并可以使用。记得定期检查GitLab官方文档,因为GitLab的安装和配置步骤可能随着新版本而变化。
822 0
|
10月前
|
安全 算法 Ubuntu
Linux(openssl)环境:编程控制让证书自签的技巧。
总结:在Linux环境中,OpenSSL是一个非常实用的工具,可以帮助我们轻松地生成自签名证书。通过上述三个简单步骤,即可为内部网络、测试环境或开发环境创建自签名证书。但在公共访问场景下,建议购买经过权威认证机构签发的证书,以避免安全警告。
480 13
|
10月前
|
存储 安全 Ubuntu
从Linux到Windows:阿里云服务器系统镜像适配场景与选择参考
阿里云为用户提供了丰富多样的服务器操作系统选择,以满足不同场景下的应用需求。目前,云服务器的操作系统镜像主要分为公共镜像、自定义镜像、共享镜像、镜像市场和社区镜像五大类。以下是对这些镜像类型的详细介绍及选择云服务器系统时需要考虑的因素,以供参考。
|
9月前
|
Linux
Linux下版本控制器(SVN) -服务器端环境搭建步骤
Linux下版本控制器(SVN) -服务器端环境搭建步骤
384 0
Linux下版本控制器(SVN) -服务器端环境搭建步骤