epoll的實現與深刻思考

    提契ios

    紙上得來終覺淺,絕知此事要躬行。服務器

    正文網絡

    前段時間寫了一篇epoll的學習文章,但沒有本身的心得總以爲比較膚淺,花了一些時間補充一個epoll的實例,並淺析一下過程當中遇到的問題。框架

    上epoll_server的例子,epoll的代碼都在這裏ssh

  1 #include<iostream>
  2 #include<stdlib.h>
  3 #include<sys/epoll.h>
  4 #include<sys/socket.h>
  5 #include<netinet/ in.h>
  6 #include<sys/types.h>
  7 #include<fcntl.h>
  8 
  9  using  namespace std;
 10  const  int PORT =  8888;
 11  const  int MAX_CLIENT_NUM =  10000;
 12  const  int MAX_LEN =  2000;
 13 
 14  bool setfdnoblock( int fd)
 15 {
 16      int flg = fcntl(fd, F_GETFL);
 17      if(flg <  0)
 18     {
 19         cout <<  " get fd flag failed " << endl;
 20          return  false;
 21     }
 22      if(fcntl(fd, F_SETFL, O_NONBLOCK | flg) <  0)
 23     {
 24          return  false;
 25     }
 26      return  true;
 27 }
 28 
 29  int CreateTcpServer( int port,  int listennum)
 30 {
 31      int fd;
 32     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 33 
 34     sockaddr_in TcpServer;
 35     bzero(&TcpServer,  sizeof(TcpServer));
 36     TcpServer.sin_family = AF_INET;
 37     TcpServer.sin_port = htons( 8888);
 38     TcpServer.sin_addr.s_addr = htonl(INADDR_ANY);
 39 
 40      int iRet = bind(fd, ( struct sockaddr*)&TcpServer,  sizeof(TcpServer));
 41      if(- 1 == iRet)
 42     {
 43     cout <<  " server bind error! " << endl;
 44      return - 1;
 45     }
 46      if(listen(fd, listennum) == - 1)
 47     {
 48         cout <<  " server listen error " << endl;
 49          return - 1;
 50     }
 51      return fd;
 52 }
 53 
 54  int main()
 55 {
 56      int Serverfd = CreateTcpServer(PORT, MAX_CLIENT_NUM);
 57      if(Serverfd == - 1)
 58     {
 59         cout <<  " server create failed " << endl;
 60     }
 61      else
 62     {
 63        cout <<  " serverfd is : " << Serverfd << endl;
 64     }
 65 
 66      int Epollfd = epoll_create(MAX_CLIENT_NUM);
 67      if(Epollfd == - 1)
 68     {
 69         cout <<  " epoll_create failed " << endl;
 70     }
 71     epoll_event ev, events[MAX_CLIENT_NUM];
 72      int nfds =  0;
 73      int client =  0;
 74      char buff[MAX_LEN];
 75     sockaddr_in CliAddr;
 76     unsigned  int iCliSize =  sizeof(CliAddr);
 77     ev.events = EPOLLIN|EPOLLOUT;
 78     ev.data.fd = Serverfd;
 79      if(!setfdnoblock(Serverfd))
 80     {
 81         cout <<  " set serverfd no_block failed " << endl;
 82     }
 83      if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev))
 84     {
 85         cout <<  " epoll add serverfd error " << endl;
 86     }
 87      while( 1)
 88     {
 89         nfds = epoll_wait(Epollfd, events, MAX_CLIENT_NUM,  100000);
 90          if(nfds == - 1)
 91         {
 92             cout <<  " error occur, exit " << endl;
 93              return - 1;
 94         }
 95          else  if( nfds ==  0)
 96         {
 97             cout <<  " epoll_wait return zero " << endl;
 98         }
 99          else
100         {
101              for( int i =  0; i < nfds; i++)
102             {
103                 cout <<  " events[i].data.fd is : " << events[i].data.fd << endl;
104                  if(events[i].data.fd == Serverfd)
105                 {
106                     cout <<  "  Serverfd received event " << endl;
107                     client = accept(Serverfd, ( struct sockaddr*)&CliAddr, &iCliSize);
108                      if(client == - 1)
109                     {
110                         cout <<  " accept error " << endl;
111                          return - 1;
112                     }
113                     ev.data.fd = client;
114                      if(!setfdnoblock(client))
115                     {
116                         cout <<  " set client fd no_block error " << endl;
117                     }
118                      if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, client, &ev))
119                     {
120                         cout <<  " epoll add client error " << endl;
121                     }
122                      else
123                     {
124                         cout <<  " success add client " << endl;
125                     }
126                 }
127                  else  if(events[i].events&EPOLLIN)
128                 {
129                     cout <<  " recv client msg " << endl;
130                      if(events[i].data.fd <  0)
131                     {
132                         cout <<  "  event[i].data.fd is smaller than zero " << endl;
133                          continue;
134                     }
135                      if(read(events[i].data.fd, buff, MAX_LEN) == - 1)
136                     {
137                         perror( " clifd read ");
138                     }
139                      else
140                     {
141                         cout <<  " read client msg suc " << endl;
142                         printf( " %s ",buff);
143                     }
144                      char resp[] =  " recv a client msg, this is resp msg ";
145                     write(events[i].data.fd, resp, strlen(resp)+ 1);
146                      // read and mod
147                  }
148                  else  if(events[i].events&EPOLLOUT)
149                 {
150                      // send and mod
151                  }
152             }
153         }
154     }
155 }

    下面是一個TCP的client例子異步

 1 #include<sys/types.h>
 2 #include<sys/socket.h>
 3 #include<netinet/ in.h>
 4 #include<stdlib.h>
 5 #include<arpa/inet.h>
 6 #include<stdio.h>
 7 #include< string.h>
 8 #include<iostream>
 9 
10  using  namespace std;
11  const  int MAX_BUFF= 2048;
12  char notify[] =  " i'm client ";
13 
14  int main()
15 {
16      int Clifd;
17      char buff[MAX_BUFF];
18     Clifd = socket(AF_INET, SOCK_STREAM,  0);
19      if(Clifd == - 1)
20     {
21         perror( " clifd socket ");
22     }
23     sockaddr_in CliSock;
24     bzero(&CliSock,  sizeof(CliSock));
25     CliSock.sin_family = AF_INET;
26     CliSock.sin_addr.s_addr = inet_addr( " 203.195.243.17 ");
27     CliSock.sin_port = htons( 8888);
28 
29      if(- 1 == connect(Clifd, ( struct sockaddr*)&CliSock,  sizeof(CliSock)))
30     {
31         perror( " clifd connect ");
32     }
33      if(write(Clifd, notify, strlen(notify)+ 1) == - 1)
34     {
35         perror( " clifd write ");
36     }
37     cout <<  " write over " << endl;
38      if(read(Clifd, buff, MAX_BUFF) == - 1)
39     {
40         perror( " clifd read ");
41     }
42      else
43     {
44         cout << buff << endl;
45     }
46 }

    比較簡單的代碼,算是epoll的典型框架,再也不解釋代碼,將編碼過程犯得幾個錯拿出來供你們借鑑:socket

    1.epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev)添加Serverfd至epoll監視列表時ev.data.fd並無設置等於serverfd,結果客戶端來鏈接的時候不停收到EPOLLIN消息,但events[i].data.fd == Serverfd卻從不成立,在epoll_ctl以前添加ev.data.fd = Serverfd解決問題,對此個人理解是epoll_ctl將第三參數Serverfd加入監視列表,並將ev結構體與Serverfd關聯起來,但epoll不會主動監測和修改ev的其餘參數,在epoll_wait時epoll監測到Serverfd上有可讀的消息就返回,並將Serverfd關聯的ev結構體賦值給events這個事件列表,因此events[i].data.fd其實就是epoll_ctl註冊時的ev.data.fd。函數

    2.當client代碼調用connect之後當即返回了鏈接創建成功的結果,此時epoll_server的accept函數尚未調用,那麼TCP的鏈接創建原理是怎樣的?學習

    從這張圖片來看,TCP的server端在listen之後會被動接受client的請求並創建鏈接,accept和創建鏈接沒有直接關係,accept只是從serverfd上獲取到請求建連的client信息,accept以前已經完成了鏈路的建立。this

    延伸話題

    同步/異步與阻塞/非阻塞

    同步/異步
    同步和異步每每和線程有關係,好比SendMsg,線程A->線程B,發送消息後須要等待其餘線程或進程的響應。
    若是SendMsg之後線程A等待線程B返回響應消息,線程才繼續處理,這就是同步
    若是SendMsg之後線程A就繼續作本身的事情,而註冊了一個回調或者響應線程來處理線程B的響應消息,這就是異步
    同步每每是阻塞的
    異步每每是非阻塞的

    阻塞/非阻塞
    阻塞read:線程將阻塞,直到有可讀的數據。
    非阻塞read:設置O_NOBLOCK之後,若是fd沒有可讀的數據,read將當即返回-1並設errno爲EAGAIN。
    在有可讀數據時阻塞與非阻塞read是同樣的。
    阻塞write:data從user-mode空間move到kernel-mode空間,以後系統完成kernel-mode到物理緩衝的處理並返回,而後阻塞IO返回。
    非阻塞write:data從user-mode空間move到kernel-mode空間,write返回。

    小結

    epoll的延伸應用、同步異步、阻塞非阻塞均可以談不少,之後寫對應的文章進行詳解。

    補充

    騰訊雲服務器真是一臺裸機,ftp服務沒有打開,默認只能ssh登錄root帳戶,man手冊用法有點奇怪,用了一天之後還報了hostname錯誤和eth1找不到網絡設備,感受不是很成熟的產品。

相關文章
相關標籤/搜索