epoll - 其一

头文件:#include <sys/epoll.h>

一.eopll相关的函数:

1.int epoll_create(int size);

  返回一个epoll句柄,参数size是可监听的最大个数

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

  参数一epfd是epoll_create()出来到句柄。

  参数二op可以取三个宏中到其一:

    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;

  参数三fd是要监听的fd。

  参数四event是设置监听到模式(ET模式,LT模式,write事件,read事件,accept事件)

    相关结构体如下:

        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)来说的。
        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

     epoll_event.data.fd是要监听到fd,一般设置epoll_event.data.fd和epoll_event.events就足够了。

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

  返回值是触发事件到个数,如返回0表示已超时。

  参数一epfd是epoll_create()出来到句柄。

  参数二events是一个epoll_event类型的数组。

  参数三maxevents的值不能大于创建epoll_create()时的size。

  参数四timeout是超时时间(毫秒,0会立即返回,-1将不确定,永久阻塞)。

4.close(int fd)

   关闭句柄。

二.代码

1.服务器

  1 #include <sys/socket.h>
  2 #include <sys/epoll.h>
  3 #include <netinet/in.h>
  4 #include <arpa/inet.h>
  5 #include <fcntl.h>
  6 #include <unistd.h>
  7 #include <stdio.h>
  8 #include <errno.h>
  9 #include <string.h>
 10 
 11 
 12 
 13 #define bool int
 14 #define false 0
 15 #define true 1
 16 
 17 #define MAXLINE 5
 18 #define OPEN_MAX 100
 19 #define LISTENQ 20
 20 #define SERV_PORT 5000
 21 #define INFTIM 1000
 22 
 23 void setnonblocking(int sock)
 24 {
 25     int opts;
 26     opts=fcntl(sock,F_GETFL);
 27     if(opts<0)
 28     {
 29         perror("fcntl(sock,GETFL)");
 30         return;
 31     }
 32     opts = opts|O_NONBLOCK;
 33     if(fcntl(sock,F_SETFL,opts)<0)
 34     {
 35         perror("fcntl(sock,SETFL,opts)");
 36         return;
 37     }
 38 }
 39 
 40 void CloseAndDisable(int sockid, struct epoll_event ee)
 41 {
 42     close(sockid);
 43     ee.data.fd = -1;
 44 }
 45 
 46 int main()
 47 {
 48     int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber;
 49     char line[MAXLINE];
 50     socklen_t clilen;
 51 
 52     struct sockaddr_in clientaddr;
 53     struct sockaddr_in serveraddr;
 54 
 55 
 56     portnumber = 5000;
 57 
 58     //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
 59 
 60     struct epoll_event ev,events[20];
 61     //生成用于处理accept的epoll专用的文件描述符
 62 
 63     epfd=epoll_create(256);
 64 
 65     listenfd = socket(AF_INET, SOCK_STREAM, 0);
 66 
 67     memset(&serveraddr, 0, sizeof(serveraddr));
 68     serveraddr.sin_family = AF_INET;
 69     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 70     serveraddr.sin_port=htons(portnumber);
 71 
 72     // bind and listen
 73     //no-blocking
 74     setnonblocking(listenfd);
 75     bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr));
 76     if(0 != listen(listenfd, LISTENQ)){
 77         perror("listen !=0");
 78     }
 79 
 80 
 81     //设置与要处理的事件相关的文件描述符
 82     ev.data.fd=listenfd;
 83     //设置要处理的事件类型
 84     ev.events=EPOLLIN|EPOLLET;
 85     //ev.events=EPOLLIN;
 86 
 87     //注册epoll事件
 88     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
 89 
 90     maxi = 0;
 91 
 92     int bOut = 0;
 93     for ( ; ; )
 94     {
 95         if (bOut == 1)
 96             break;
 97         //等待epoll事件的发生
 98 
 99         nfds=epoll_wait(epfd,events,20,-1);
100         //处理所发生的所有事件
101 
102         printf("\nepoll_wait returns\n");
103 
104         for(i=0;i<nfds;++i)
105         {
106             if(events[i].data.fd==listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
107             {
108                 connfd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen);
109                 if(connfd<0){
110                     perror("connfd<0");
111                     return (1);
112                 }
113                 
114 
115                 char *str = inet_ntoa(clientaddr.sin_addr);
116 
117                 printf("accapt a connection from %s\n",str);
118                 //设置用于读操作的文件描述符
119 
120                 setnonblocking(connfd);
121                 ev.data.fd=connfd;
122                 //设置用于注测的读操作事件
123 
124                 ev.events=EPOLLIN | EPOLLET;
125                 //ev.events=EPOLLIN;
126 
127                 //注册ev
128                 epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
129             }
130             else if(events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。
131             {
132 
133                 printf("EPOLLIN\n");
134                 if ( (sockfd = events[i].data.fd) < 0)
135                     continue;
136 
137                 char * head = line;
138                 int recvNum = 0;
139                 int count = 0;
140                 bool bReadOk = false;
141                 while(1)
142                 {
143                     // 确保sockfd是nonblocking的
144                     recvNum = recv(sockfd, head + count, MAXLINE, 0);
145                     if(recvNum < 0)
146                     {
147                         if(errno == EAGAIN)
148                         {
149                             // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
150                             // 在这里就当作是该次事件已处理处.
151                             bReadOk = true;
152                             break;
153                         }
154                         else if (errno == ECONNRESET)
155                         {
156                                 // 对方发送了RST
157                                 CloseAndDisable(sockfd, events[i]);
158 
159                                 printf("counterpart send out RST\n");
160                                 break;
161                          }
162                         else if (errno == EINTR)
163                         {
164                             // 被信号中断
165                             continue;
166                         }
167                         else
168                         {
169                             //其他不可弥补的错误
170                             CloseAndDisable(sockfd, events[i]);
171 //                            cout << "unrecovable error\n";
172                             printf("unrecovable error\n");
173                             break;
174                         }
175                    }
176                    else if( recvNum == 0)
177                    {
178                         // 这里表示对端的socket已正常关闭.发送过FIN了。
179                         CloseAndDisable(sockfd, events[i]);
180 
181                         printf("counterpart has shut off\n");
182                         break;
183                    }
184 
185                    // recvNum > 0
186                     count += recvNum;
187                    if ( recvNum == MAXLINE)
188                    {
189                        continue;   // 需要再次读取
190                    }
191                    else // 0 < recvNum < MAXLINE
192                    {
193                        // 安全读完
194                        bReadOk = true;
195                        break; // 退出while(1),表示已经全部读完数据
196                    }
197                 }
198 
199                 if (bReadOk == true)
200                 {
201                     // 安全读完了数据
202                     line[count] = '\0';
203 
204 //                    cout << "we have read from the client : " << line;
205                     printf("we have read from the client : %s",line);
206                     //设置用于写操作的文件描述符
207 
208                     ev.data.fd=sockfd;
209                     //设置用于注测的写操作事件
210 
211                     ev.events = EPOLLOUT | EPOLLET;
212                     //修改sockfd上要处理的事件为EPOLLOUT
213 
214                     epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
215                 }
216             }
217             else if(events[i].events & EPOLLOUT) // 如果有数据发送
218             {
219                 const char str[100] = "hello from epoll : this is a long string which may be cut by the net\n";
220                 int strlen = sizeof(str);
221                 printf("Write %s\n",str);
222                 sockfd = events[i].data.fd;
223 
224                 bool bWritten = false;
225                 int writenLen = 0;
226                 int count = 0;
227                 char * head = line;
228                 while(1)
229                 {
230                         // 确保sockfd是非阻塞的
231                         writenLen = send(sockfd, str + count, strlen - count/*MAXLINE*/, 0);
232                         if (writenLen == -1)
233                         {
234                             if (errno == EAGAIN)
235                             {
236                                 // 对于nonblocking 的socket而言,这里说明了已经全部发送成功了
237                                 bWritten = true;
238                                 break;
239                             }
240                             else if(errno == ECONNRESET)
241                             {
242                                 // 对端重置,对方发送了RST
243                                 CloseAndDisable(sockfd, events[i]);
244                                 printf("counterpart send out RST\n");
245                                 break;
246                             }
247                             else if (errno == EINTR)
248                             {
249                                 // 被信号中断
250                                 continue;
251                             }
252                             else
253                             {
254                                 // 其他错误
255                             }
256                         }
257 
258                         if (writenLen == 0)
259                         {
260                             // 这里表示对端的socket已正常关闭.
261                             CloseAndDisable(sockfd, events[i]);
262 
263                             printf("counterpart has shut off\n");
264                             break;
265                         }
266 
267                         // 以下的情况是writenLen > 0
268                         count += writenLen;
269                         if(count < strlen)
270                         {
271                             // 可能还没有写完
272                             continue;
273                         }
274                         else // 0 < writenLen < MAXLINE
275                         {
276                             // 已经写完了
277                             bWritten = true;
278                             break; // 退出while(1)
279                         }
280                 }
281 
282                 if (bWritten == true)
283                 {
284                     //设置用于读操作的文件描述符
285                     ev.data.fd=sockfd;
286 
287                     //设置用于注测的读操作事件
288                     ev.events=EPOLLIN | EPOLLET;
289 
290                     epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
291                 }
292             }
293         }
294     }
295     return 0;
296 }

2.客户端

 1 #include <sys/socket.h>
 2 #include <sys/epoll.h>
 3 #include <netinet/in.h>
 4 #include <arpa/inet.h>
 5 #include <fcntl.h>
 6 #include <unistd.h>
 7 #include <stdio.h>
 8 #include <errno.h>
 9 #include <string.h>
10 #include <stdlib.h>
11 #include <pthread.h>
12 
13 
14 
15 
16 #define bool int
17 #define false 0
18 #define true 1
19 
20 
21 #define MAXLINE 100
22 #define OPEN_MAX 100
23 #define LISTENQ 20
24 #define SERV_PORT 5000
25 #define INFTIM 1000
26 
27 
28 
29 
30 int                    sockfd;
31 char                recvline[MAXLINE + 1] = "\0";
32 pthread_t threadid;
33     
34 void *m_thread(){
35     int len=0;
36     while(1){
37         len = recv(sockfd,recvline,30,0);
38         recvline[len] = '\0';
39         printf("%s",recvline);
40         if(len == -1)
41             return;
42     }
43 }
44 
45 int
46 main(int argc, char **argv)
47 {
48 
49 
50     struct sockaddr_in    servaddr;
51     
52     if (argc != 2){
53         printf("usage: a.out <IPaddress>\n");
54         return 0;
55     }
56 
57     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
58 
59         printf("socket error\n");
60         return 0;
61     }
62 
63 
64 
65 
66     bzero(&servaddr, sizeof(servaddr));
67     servaddr.sin_family = AF_INET;
68     servaddr.sin_port   = htons(5000);   
69     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
70         printf("inet_pton error for %s\n", argv[1]);
71 
72     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
73         printf("connect error\n");
74     if(pthread_create(&threadid, NULL, m_thread, NULL) != 0)
75         perror("create Thread:");
76 
77     char input[100];
78     while (fgets(input, 100, stdin) != EOF)
79     {
80         write(sockfd, input, strlen(input));
81 
82         int n = 0;
83         int count = 0;
84         
85     }
86     exit(0);
87 }

 

参考:

blog.csdn.net/ljx0305/article/details/4065058

http://www.cnblogs.com/aicro/archive/2012/12/27/2836170.html

http://blog.csdn.net/dodo_check/article/details/8553265

posted on 2016-08-04 17:50  JinFei_Su  阅读(489)  评论(0)    收藏  举报

导航