程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> epoll,epoll模型

epoll,epoll模型

編輯:關於C語言

epoll,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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved