天天看點

socket程式設計 及select poll epoll示例

[cpp]

​​ view plain​​

​​ copy​​

1、關于位元組排序    網際協定采用大端位元組序,來傳輸多位元組整數。    系統提供了轉換的宏定義,如果主機與網際協定相同,則宏定義為空。

2、用戶端    socket -> connect(阻塞,三次握手)-> rcv

3、伺服器端    socket -> bind -> listen -> accept(阻塞,三次握手)-> send4、函數介紹    

     a..socket        

          1)函數原型 int socket(int family, int type, int protocol)        

          2)參數:                family: 協定族AF_INET,IPv4協定 ...            type : type 套接字類型SOCK_STREAM 位元組流套接字            protocol: IPPROCO_TCP IPPROCO_UDP                     IPPROCO_SCTP       

          3)傳回值            成功:傳回套接字元            錯誤:傳回INVALID_SOCKET(-1)        

         4)示例

[cpp]

​​ view plain​​

​​ copy​​

  1. #include <netinet/in.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. int
  5. {  
  6. int
  7. struct
  8. if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  9.     {  
  10. return
  11.     }  
  12. }  

    b..connect

        1)函數原型 int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)

        2)參數:    

            sockfd: socket 函數傳回的套接字描述符

            servaddr : 伺服器的IP和端口

            addrlen: 長度(sizeof(servaddr))

        3)傳回值

            成功:0

            錯誤:傳回INVALID_SOCKET(-1)

        4)示例

[cpp]

​​ view plain​​

​​ copy​​

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <netinet/in.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. int
  7. {  
  8. int
  9. struct
  10. if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  11.     {  
  12. "socket error\n");  
  13. return
  14.     }  
  15. sizeof(servaddr));  
  16. "192.168.0.218");  
  17.     servaddr.sin_family = AF_INET;  
  18.     servaddr.sin_port = htons(55000);  
  19. if(connect(socketfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0)  
  20.     {  
  21. "connect error\n");  
  22.     }  
  23. return
  24. }  

    c..bind

        1)函數原型 int bind(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)

        2)參數:    

            sockfd: socket 函數傳回的套接字描述符

            servaddr : 伺服器的IP和端口

            addrlen: 長度(sizeof(servaddr))

        3)傳回值

            成功:0

            錯誤:傳回INVALID_SOCKET(-1)

    d..listen

        1)函數原型 int listen(int sockfd, int backlog)

        2)參數:    

            sockfd: socket 函數傳回的套接字描述符

            backlog : 核心中套接字排隊的最大個數

        3)傳回值

            成功:0

            錯誤:傳回INVALID_SOCKET

    e..accept

        1)函數原型 int accept(int sockfd, const struct sockaddr *servaddr, socklen_t *addrlen)

        2)參數:    

            sockfd: socket 函數傳回的套接字描述符

        3)傳回值

            servaddr : 客戶程序的IP和端口(可設為null)

            addrlen: 長度(sizeof(servaddr))(可設為null)

            成功:從監聽套接字傳回已連接配接套接字

            錯誤:

            如果對客戶資訊不感興趣,後兩個參數可以置空。

        4)示例

[cpp]

​​ view plain​​

​​ copy​​

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <netinet/in.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. int
  7. {  
  8. int
  9. int
  10. int
  11. struct
  12. struct
  13. char
  14. "accept started\n");  
  15. //socket    
  16. if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  17.     {  
  18. "socket error\n");  
  19. return
  20.     }  
  21. sizeof(servaddr));  
  22.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  23.     servaddr.sin_family = AF_INET;  
  24.     servaddr.sin_port = htons(59000);  
  25. //bind
  26. if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)  
  27.     {  
  28. "bind error\n");  
  29. //return -1;
  30.     }  
  31. //listen
  32.     listen(listenfd, 5);  
  33. //accept
  34.     socketfd = accept(listenfd, NULL, NULL);  
  35. while(1)  
  36.     {  
  37. "start receive %d...\n", count++);  
  38. sizeof(readbuf), 0);  
  39.         nread = recv(socketfd, readbuf, 10, 0);  
  40. if(nread>0)  
  41.         {  
  42. '\0';  
  43. "receiveed %s, nread = %d\n\n", readbuf, nread);  
  44.         }  
  45.     }  
  46. return
  47. }  

    /**************************************************************

    從 I/O 事件分派機制來看,使用 select()是不合适的,因為它所支援的并發連接配接數有限(通

    常在 1024 個以内)。如果考慮性能,poll()也是不合适的,盡管它可以支援的較高的 TCP 并發

    數,但是由于其采用“輪詢”機制,當并發數較高時,其運作效率相當低,并可能存在 I/O 事

    件分派不均,導緻部分 TCP 連接配接上的 I/O 出現“饑餓”現象。而如果使用 epoll 或 AIO,則沒

    有上述問題(早期 Linux 核心的 AIO 技術實作是通過在核心中為每個 I/O 請求建立一個線程來

    實作的,這種實作機制在高并發 TCP 連接配接的情形下使用其實也有嚴重的性能問題。但在最新的

    Linux 核心中,AIO 的實作已經得到改進)。

    支援一個程序打開大數目的 socket 描述符(FD)select 最不能忍受的是一個程序所打開的

    FD 是有一定限制的,由 FD_SETSIZE 設定,預設值是 2048。對于那些需要支援的上萬連接配接數目

    的 IM 伺服器來說顯然太少了。

    這時候你一是可以選擇修改這個宏然後重新編譯核心,不過資料

    也同時指出這樣會帶來網絡效率的下降,二是可以選擇多程序的解決方案(傳統的 Apache 方

    案),不過雖然 linux 上面建立程序的代價比較小,但仍舊是不可忽視的,加上程序間資料同步

    遠比不上線程間同步的高效,是以也不是一種完美的方案。不過 epoll 則沒有這個限制,它所

    支援的 FD 上限是最大可以打開檔案的數目,這個數字一般遠大于 2048,舉個例子,在 1GB 記憶體

    的機器上大約是 10 萬左右,具體數目可以 cat /proc/sys/fs/file-max 察看,一般來說這個數

    目和系統記憶體關系很大。

    ******************************************************************/        

5. select函數

    1)函數原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

    2)參數:    

        sockfd: socket 函數傳回的套接字描述符

        readfds : 讀描述符集合

        writefds: 寫描述符集合

        errorfds: 錯誤描述符集合

        timeout:  逾時

    3)傳回值

        成功:傳回值 0:無 >0:描述符就緒的總位數

        錯誤:傳回INVALID_SOCKET(-1)

    4)包含頭檔案: include <sys/select.h> include <sys/time.h>

    5)示例

[cpp]

​​ view plain​​

​​ copy​​

  1. /* 實作功能:通過select處理多個socket
  2.  * 監聽一個端口,監聽到有連結時,添加到select的w.
  3.  */
  4. #include "select.h"
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/socket.h>
  8. #include <sys/select.h>
  9. #include <sys/time.h>
  10. #include <netinet/in.h>
  11. typedef struct
  12. int
  13. struct sockaddr_in addr; /* client's address information */
  14. } CLIENT;  
  15. #define MYPORT 59000
  16. //最多處理的connect
  17. #define BACKLOG 5
  18. //最多處理的connect
  19. CLIENT client[BACKLOG];  
  20. //目前的連接配接數
  21. int
  22. //資料接受 buf
  23. #define REVLEN 10
  24. char
  25. //顯示目前的connection
  26. void
  27. int
  28. {  
  29. int
  30. int
  31.     fd_set readfds, writefds;  
  32. int
  33. struct
  34. struct
  35. struct
  36. for(i=0; i<BACKLOG; i++)  
  37.     {  
  38.         client[i].fd = -1;  
  39.     }  
  40. //socket
  41. if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  42.     {  
  43. "socket error\n");  
  44. return
  45.     }  
  46. sizeof(server_addr));  
  47.     server_addr.sin_family  =  AF_INET;  
  48.     server_addr.sin_port = htons(MYPORT);  
  49.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  50. //bind
  51. if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  52.     {  
  53. "bind error\n");  
  54. return
  55.     }  
  56. //listen
  57. if(listen(sockListen, 5) < 0)  
  58.     {  
  59. "listen error\n");  
  60. return
  61.     }  
  62. for(i=0; i<BACKLOG; i++)  
  63.     {  
  64.         client[i].fd = -1;  
  65.     }  
  66. //select
  67. while(1)  
  68.     {  
  69.         FD_ZERO(&readfds);  
  70.         FD_SET(sockListen, &readfds);  
  71.         sockMax = sockListen;  
  72. //加入client
  73. for(i=0; i<BACKLOG; i++)  
  74.         {  
  75. if(client[i].fd >0)  
  76.             {  
  77.                 FD_SET(client[i].fd, &readfds);  
  78. if(sockMax<client[i].fd)   
  79.                     sockMax = client[i].fd;  
  80.             }  
  81.         }  
  82.         timeout.tv_sec=3;                  
  83.         timeout.tv_usec=0;  
  84. //select
  85. int)sockMax+1, &readfds, NULL, NULL, &timeout);  
  86. if(ret < 0)  
  87.         {  
  88. "select error\n");  
  89. break;  
  90.         }  
  91. else if(ret == 0)  
  92.         {  
  93. "timeout ...\n");  
  94. continue;  
  95.         }  
  96. "test111\n");  
  97. //讀取資料
  98. for(i=0; i<BACKLOG; i++)  
  99.         {  
  100. if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds))  
  101.             {  
  102. if(recvLen != REVLEN)  
  103.                 {  
  104. while(1)  
  105.                     {  
  106. //recv資料
  107. char
  108. if(ret == 0)  
  109.                         {  
  110.                             client[i].fd = -1;  
  111.                             recvLen = 0;  
  112. break;  
  113.                         }  
  114. else if(ret < 0)  
  115.                         {  
  116.                             client[i].fd = -1;  
  117.                             recvLen = 0;  
  118. break;  
  119.                         }  
  120. //資料接受正常
  121.                         recvLen = recvLen+ret;  
  122. if(recvLen<REVLEN)  
  123.                         {  
  124. continue;  
  125.                         }  
  126. else
  127.                         {  
  128. //資料接受完畢
  129. "%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf);  
  130. //close(client[i].fd);
  131. //client[i].fd = -1;
  132.                             recvLen = 0;  
  133. break;  
  134.                         }  
  135.                     }  
  136.                 }  
  137.             }  
  138.         }  
  139. //如果可讀
  140. if(FD_ISSET(sockListen, &readfds))  
  141.         {  
  142. "isset\n");  
  143. //(struct sockaddr*)&client_addr
  144. if(sockSvr == -1)  
  145.             {  
  146. "accpet error\n");  
  147.             }  
  148. else
  149.             {  
  150.                 currentClient++;  
  151.             }  
  152. for(i=0; i<BACKLOG; i++)  
  153.             {  
  154. if(client[i].fd < 0)  
  155.                 {  
  156.                     client[i].fd = sockSvr;  
  157.                     client[i].addr = client_addr;  
  158. "You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) );  
  159. break;  
  160.                 }  
  161.             }  
  162. //close(sockListen);
  163.         }  
  164.     }  
  165. "test\n");  
  166. return
  167. }  
  168. //顯示目前的connection
  169. void
  170. {  
  171. int
  172. "client count = %d\n", currentClient);  
  173. for(i=0; i<BACKLOG; i++)  
  174.     {  
  175. "[%d] = %d", i, client[i].fd);  
  176.     }  
  177. "\n");  
  178. }  

6. poll函數

    1)函數原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

    2)參數:    

        sockfd: socket 函數傳回的套接字描述符

        readfds : 讀描述符集合

        writefds: 寫描述符集合

        errorfds: 錯誤描述符集合

        timeout:  逾時

    3)傳回值

        成功:傳回值 0:無 >0:描述符就緒的總位數

        錯誤:傳回INVALID_SOCKET(-1)

    4)包含頭檔案: include <sys/select.h> include <sys/time.h>

    5) 示例

[cpp]

​​ view plain​​

​​ copy​​

  1. /* 實作功能:通過poll, 處理多個socket
  2.  * 監聽一個端口,監聽到有連結時,添加到poll.
  3.  */
  4. #include "select.h"
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/socket.h>
  9. #include <poll.h>
  10. #include <sys/time.h>
  11. #include <netinet/in.h>
  12. typedef struct
  13. int
  14. struct sockaddr_in addr; /* client's address information */
  15. } CLIENT;  
  16. #define MYPORT 59000
  17. //最多處理的connect
  18. #define BACKLOG 5
  19. //目前的連接配接數
  20. int
  21. //資料接受 buf
  22. #define REVLEN 10
  23. char
  24. #define OPEN_MAX 1024
  25. int
  26. {  
  27. int
  28. int
  29.     fd_set readfds, writefds;  
  30. int
  31. int
  32. struct
  33. struct
  34. struct
  35. //socket
  36. if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  37.     {  
  38. "socket error\n");  
  39. return
  40.     }  
  41. sizeof(server_addr));  
  42.     server_addr.sin_family  =  AF_INET;  
  43.     server_addr.sin_port = htons(MYPORT);  
  44.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  45. //bind
  46. if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  47.     {  
  48. "bind error\n");  
  49. return
  50.     }  
  51. //listen
  52. if(listen(sockListen, 5) < 0)  
  53.     {  
  54. "listen error\n");  
  55. return
  56.     }  
  57. //clientfd 初始化
  58.     clientfd[0].fd = sockListen;  
  59. //POLLRDNORM;
  60.     sockMax = 0;  
  61. for(i=1; i<OPEN_MAX; i++)  
  62.     {  
  63.         clientfd[i].fd = -1;  
  64.     }  
  65. //select
  66. while(1)  
  67.     {  
  68.         timeout=3000;                  
  69. //select
  70.         ret = poll(clientfd, sockMax+1, timeout);  
  71. if(ret < 0)  
  72.         {  
  73. "select error\n");  
  74. break;  
  75.         }  
  76. else if(ret == 0)  
  77.         {  
  78. "timeout ...\n");  
  79. continue;  
  80.         }  
  81. if (clientfd[0].revents & POLLIN)//POLLRDNORM
  82.         {  
  83. //(struct sockaddr*)&client_addr
  84. if(sockSvr == -1)  
  85.             {  
  86. "accpet error\n");  
  87.             }  
  88. else
  89.             {  
  90.                 currentClient++;  
  91.             }  
  92. for(i=0; i<OPEN_MAX; i++)  
  93.             {  
  94. if(clientfd[i].fd<0)  
  95.                 {  
  96.                     clientfd[i].fd = sockSvr;  
  97. break;  
  98.                 }  
  99.             }  
  100. if(i==OPEN_MAX)  
  101.             {  
  102. "too many connects\n");  
  103. return
  104.             }  
  105. //POLLRDNORM;
  106. if(i>sockMax)  
  107.                 sockMax = i;  
  108.         }  
  109. //讀取資料
  110. for(i=1; i<=sockMax; i++)  
  111.         {  
  112. if(clientfd[i].fd < 0)  
  113. continue;  
  114. if (clientfd[i].revents & (POLLIN | POLLERR))//POLLRDNORM
  115.             {  
  116. if(recvLen != REVLEN)  
  117.                 {  
  118. while(1)  
  119.                     {  
  120. //recv資料
  121. char
  122. if(ret == 0)  
  123.                         {  
  124.                             clientfd[i].fd = -1;  
  125.                             recvLen = 0;  
  126. break;  
  127.                         }  
  128. else if(ret < 0)  
  129.                         {  
  130.                             clientfd[i].fd = -1;  
  131.                             recvLen = 0;  
  132. break;  
  133.                         }  
  134. //資料接受正常
  135.                         recvLen = recvLen+ret;  
  136. if(recvLen<REVLEN)  
  137.                         {  
  138. continue;  
  139.                         }  
  140. else
  141.                         {  
  142. //資料接受完畢
  143. "buf = %s\n",  recvBuf);  
  144. //close(client[i].fd);
  145. //client[i].fd = -1;
  146.                             recvLen = 0;  
  147. break;  
  148.                         }  
  149.                     }  
  150.                 }  
  151.             }  
  152.         }  
  153.     }  
  154. return
  155. }  

6. epoll函數

        2. 常用模型的缺點

        如果不擺出來其他模型的缺點,怎麼能對比出 Epoll 的優點呢。

        2.1 PPC/TPC 模型

        這兩種模型思想類似,就是讓每一個到來的連接配接一邊自己做事去,别再來煩我 。隻是 PPC 是為它開了一個程序,而 TPC 開了一個線程。可是别煩我是有代價的,它要時間和空間啊,連接配接多了之後,那麼多的程序 / 線程切換,這開銷就上來了;是以這類模型能接受的最大連接配接數都不會高,一般在幾百個左右。

        2.2 select 模型

        1. 最大并發數限制,因為一個程序所打開的 FD (檔案描述符)是有限制的,由 FD_SETSIZE 設定,預設值是 1024/2048 ,是以 Select 模型的最大并發數就被相應限制了。自己改改這個 FD_SETSIZE ?想法雖好,可是先看看下面吧 …

        2. 效率問題, select 每次調用都會線性掃描全部的 FD 集合,這樣效率就會呈現線性下降,把 FD_SETSIZE 改大的後果就是,大家都慢慢來,什麼?都逾時了??!!

        3. 核心 / 使用者空間 記憶體拷貝問題,如何讓核心把 FD 消息通知給使用者空間呢?在這個問題上 select 采取了記憶體拷貝方法。

        2.3 poll 模型

        基本上效率和 select 是相同的, select 缺點的 2 和 3 它都沒有改掉。

        3. Epoll 的提升

        把其他模型逐個批判了一下,再來看看 Epoll 的改進之處吧,其實把 select 的缺點反過來那就是 Epoll 的優點了。

        3.1. Epoll 沒有最大并發連接配接的限制,上限是最大可以打開檔案的數目,這個數字一般遠大于 2048, 一般來說這個數目和系統記憶體關系很大 ,具體數目可以 cat /proc/sys/fs/file-max 察看。

        3.2. 效率提升, Epoll 最大的優點就在于它隻管你“活躍”的連接配接 ,而跟連接配接總數無關,是以在實際的網絡環境中, Epoll 的效率就會遠遠高于 select 和 poll 。

        3.3. 記憶體拷貝, Epoll 在這點上使用了“共享記憶體 ”,這個記憶體拷貝也省略了。

        4. Epoll 為什麼高效

        Epoll 的高效和其資料結構的設計是密不可分的,這個下面就會提到。

        首先回憶一下 select 模型,當有 I/O 事件到來時, select 通知應用程式有事件到了快去處理,而應用程式必須輪詢所有的 FD 集合,測試每個 FD 是否有事件發生,并處理事件;代碼像下面這樣:

        int res = select(maxfd+1, &readfds, NULL, NULL, 120);

        if (res > 0)

        {

            for (int i = 0; i < MAX_CONNECTION; i++)

            {

                if (FD_ISSET(allConnection[i], &readfds))

                {

                    handleEvent(allConnection[i]);

                }

            }

        }

        // if(res == 0) handle timeout, res < 0 handle error

        Epoll 不僅會告訴應用程式有I/0 事件到來,還會告訴應用程式相關的資訊,這些資訊是應用程式填充的,是以根據這些資訊應用程式就能直接定位到事件,而不必周遊整個FD 集合。

        int res = epoll_wait(epfd, events, 20, 120);

        for (int i = 0; i < res;i++)

        {

            handleEvent(events[n]);

        }

        5. Epoll 關鍵資料結構

        前面提到 Epoll 速度快和其資料結構密不可分,其關鍵資料結構就是:

        struct epoll_event {

            __uint32_t events;      // Epoll events

            epoll_data_t data;      // User data variable

        };

        typedef union epoll_data {

            void *ptr;

            int fd;

            __uint32_t u32;

            __uint64_t u64;

        } epoll_data_t;

        可見 epoll_data 是一個 union 結構體 , 借助于它應用程式可以儲存很多類型的資訊 :fd 、指針等等。有了它,應用程式就可以直接定位目标了。

        6. 使用 Epoll

        既然 Epoll 相比 select 這麼好,那麼用起來如何呢?會不會很繁瑣啊 … 先看看下面的三個函數吧,就知道 Epoll 的易用了。

        int epoll_create(int size);

        生成一個 Epoll 專用的檔案描述符,其實是申請一個核心空間,用來存放你想關注的 socket fd 上是否發生以及發生了什麼事件。 size 就是你在這個 Epoll fd 上能關注的最大 socket fd 數,大小自定,隻要記憶體足夠。

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

        控制某個 Epoll 檔案描述符上的事件:注冊、修改、删除。其中參數 epfd 是 epoll_create() 建立 Epoll 專用的檔案描述符。相對于 select 模型中的 FD_SET 和 FD_CLR 宏。

        op:EPOLL_CTL_ADD

                 Register the target file descriptor fd on the epoll instance 

              EPOLL_CTL_MOD

                  Change the event event associated with the target file descriptor fd.

              EPOLL_CTL_DEL

                   Remove  (deregister)  the  target  file descriptor fd from the epoll instance

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

        等待 I/O 事件的發生;參數說明:

        epfd: 由 epoll_create() 生成的 Epoll 專用的檔案描述符;

        epoll_event: 用于回傳代處理事件的數組;

        maxevents: 每次能處理的事件數;

        timeout: 等待 I/O 事件發生的逾時值,機關 ms

        傳回發生事件數。

        相對于 select 模型中的 select 函數。

  1. /* 實作功能:通過epoll, 處理多個socket
  2.  * 監聽一個端口,監聽到有連結時,添加到epoll_event
  3.  */
  4. #include "select.h"
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/socket.h>
  9. #include <poll.h>
  10. #include <sys/epoll.h>
  11. #include <sys/time.h>
  12. #include <netinet/in.h>
  13. typedef struct
  14. int
  15. struct sockaddr_in addr; /* client's address information */
  16. } CLIENT;  
  17. #define MYPORT 59000
  18. //最多處理的connect
  19. #define MAX_EVENTS 500
  20. //目前的連接配接數
  21. int
  22. //資料接受 buf
  23. #define REVLEN 10
  24. char
  25. //EPOLL相關 
  26. //epoll描述符
  27. int
  28. //事件數組
  29. struct
  30. void AcceptConn(int
  31. void RecvData(int
  32. int
  33. {  
  34. int
  35. int
  36.     fd_set readfds, writefds;  
  37. int
  38. int
  39. struct
  40. struct
  41. //socket
  42. if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  43.     {  
  44. "socket error\n");  
  45. return
  46.     }  
  47. sizeof(server_addr));  
  48.     server_addr.sin_family  =  AF_INET;  
  49.     server_addr.sin_port = htons(MYPORT);  
  50.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  51. //bind
  52. if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  53.     {  
  54. "bind error\n");  
  55. return
  56.     }  
  57. //listen
  58. if(listen(sockListen, 5) < 0)  
  59.     {  
  60. "listen error\n");  
  61. return
  62.     }  
  63. //1. epoll 初始化
  64.     epollfd = epoll_create(MAX_EVENTS);  
  65. struct
  66.     event.events = EPOLLIN|EPOLLET;  
  67.     event.data.fd = sockListen;  
  68. //2. epoll_ctrl
  69. if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0)  
  70.     {  
  71. "epoll add fail : fd = %d\n", sockListen);  
  72. return
  73.     }  
  74. //epoll
  75. while(1)  
  76.     {  
  77.         timeout=3000;                  
  78. //3. epoll_wait
  79. int
  80. if(ret < 0)  
  81.         {  
  82. "epoll error\n");  
  83. break;  
  84.         }  
  85. else if(ret == 0)  
  86.         {  
  87. "timeout ...\n");  
  88. continue;  
  89.         }  
  90. //直接擷取了事件數量,給出了活動的流,這裡是和poll差別的關鍵
  91. int
  92. for(n=0; n<ret; n++)  
  93.         {  
  94. //錯誤退出
  95. if
  96.                 (eventList[n].events & EPOLLHUP) ||  
  97.                 !(eventList[n].events & EPOLLIN))  
  98.             {  
  99. "epoll error\n");  
  100.               close (eventList[n].data.fd);  
  101. return
  102.             }  
  103. if
  104.             {  
  105.                 AcceptConn(sockListen);  
  106. else{  
  107.                 RecvData(eventList[n].data.fd);  
  108. //不删除
  109. //   epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent);
  110.             }  
  111.         }  
  112.     }  
  113.     close(epollfd);  
  114.     close(sockListen);  
  115. "test\n");  
  116. return
  117. }  
  118. /**************************************************
  119. 函數名:AcceptConn
  120. 功能:接受用戶端的連結
  121. 參數:srvfd:監聽SOCKET
  122. ***************************************************/
  123. void AcceptConn(int
  124. {  
  125. struct
  126. sizeof(struct
  127.     bzero(&sin, len);  
  128. int confd = accept(srvfd, (struct
  129. if
  130.     {  
  131. "bad accept\n");  
  132. return;  
  133. else
  134.     {  
  135. "Accept Connection: %d", confd);  
  136.     }  
  137. //setnonblocking(confd);
  138. //4. epoll_wait
  139. //将建立立的連接配接添加到EPOLL的監聽中
  140. struct
  141.     event.data.fd = confd;  
  142.     event.events =  EPOLLIN|EPOLLET;  
  143.     epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event);  
  144. }  
  145. //讀取資料
  146. void RecvData(int
  147. {  
  148. int
  149. int
  150.     memset(recvBuf, 0, REVLEN);  
  151. "RecvData function\n");  
  152. if(recvLen != REVLEN)  
  153.     {  
  154. while(1)  
  155.         {  
  156. //recv資料
  157. char
  158. if(ret == 0)  
  159.             {  
  160.                 recvLen = 0;  
  161. break;  
  162.             }  
  163. else if(ret < 0)  
  164.             {  
  165.                 recvLen = 0;  
  166. break;  
  167.             }  
  168. //資料接受正常
  169.             recvLen = recvLen+ret;  
  170. if(recvLen<REVLEN)  
  171.             {  
  172. continue;  
  173.             }  
  174. else
  175.             {  
  176. //資料接受完畢
  177. "buf = %s\n",  recvBuf);  
  178.                 recvLen = 0;  
  179. break;  
  180.             }  
  181.         }  
  182.     }  
  183. "content is %s", recvBuf);