socket編程 及select poll epoll示例

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)示例

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


        
    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)示例
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <netinet/in.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6.   
  7. int main()  
  8. {  
  9.     int socketfd;  
  10.     struct sockaddr_in servaddr;  
  11.   
  12.     if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  13.     {  
  14.         printf("socket error\n");  
  15.         return -1;  
  16.     }  
  17.   
  18.     bzero(&servaddr, sizeof(servaddr));  
  19.   
  20.     servaddr.sin_addr.s_addr = inet_addr("192.168.0.218");  
  21.     servaddr.sin_family = AF_INET;  
  22.     servaddr.sin_port = htons(55000);  
  23.   
  24.     if(connect(socketfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0)  
  25.     {  
  26.         printf("connect error\n");  
  27.     }  
  28.   
  29.     return 0;  
  30. }  

    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)示例

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

    /**************************************************************
    從 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)示例
  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.   
  12. typedef struct _CLIENT{  
  13.     int fd;  
  14.     struct sockaddr_in addr; /* client's address information */  
  15. } CLIENT;  
  16.   
  17. #define MYPORT 59000  
  18.   
  19. //最多處理的connect  
  20. #define BACKLOG 5  
  21.   
  22. //最多處理的connect  
  23. CLIENT client[BACKLOG];  
  24.   
  25. //當前的連接數  
  26. int currentClient = 0;   
  27.   
  28. //數據接受 buf  
  29. #define REVLEN 10  
  30. char recvBuf[REVLEN];  
  31. //顯示當前的connection  
  32. void showClient();  
  33.   
  34. int main()  
  35. {  
  36.     int i, ret, sinSize;  
  37.     int recvLen = 0;  
  38.     fd_set readfds, writefds;  
  39.     int sockListen, sockSvr, sockMax;  
  40.     struct timeval timeout;  
  41.     struct sockaddr_in server_addr;  
  42.     struct sockaddr_in client_addr;  
  43.   
  44.     for(i=0; i<BACKLOG; i++)  
  45.     {  
  46.         client[i].fd = -1;  
  47.     }  
  48.   
  49.     //socket  
  50.     if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  51.     {  
  52.         printf("socket error\n");  
  53.         return -1;  
  54.     }  
  55.   
  56.     bzero(&server_addr, sizeof(server_addr));  
  57.     server_addr.sin_family  =  AF_INET;  
  58.     server_addr.sin_port = htons(MYPORT);  
  59.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  60.   
  61.     //bind  
  62.     if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  63.     {  
  64.         printf("bind error\n");  
  65.         return -1;  
  66.     }  
  67.   
  68.     //listen  
  69.     if(listen(sockListen, 5) < 0)  
  70.     {  
  71.         printf("listen error\n");  
  72.         return -1;  
  73.     }  
  74.   
  75.     for(i=0; i<BACKLOG; i++)  
  76.     {  
  77.         client[i].fd = -1;  
  78.     }  
  79.   
  80.     //select  
  81.     while(1)  
  82.     {  
  83.         FD_ZERO(&readfds);  
  84.         FD_SET(sockListen, &readfds);  
  85.         sockMax = sockListen;  
  86.       
  87.         //加入client  
  88.         for(i=0; i<BACKLOG; i++)  
  89.         {  
  90.             if(client[i].fd >0)  
  91.             {  
  92.                 FD_SET(client[i].fd, &readfds);  
  93.                 if(sockMax<client[i].fd)   
  94.                     sockMax = client[i].fd;  
  95.             }  
  96.         }  
  97.           
  98.         timeout.tv_sec=3;                  
  99.         timeout.tv_usec=0;  
  100.         //select  
  101.         ret = select((int)sockMax+1, &readfds, NULL, NULL, &timeout);  
  102.         if(ret < 0)  
  103.         {  
  104.             printf("select error\n");  
  105.             break;  
  106.         }  
  107.         else if(ret == 0)  
  108.         {  
  109.             printf("timeout ...\n");  
  110.             continue;  
  111.         }  
  112.         printf("test111\n");  
  113.       
  114.         //讀取數據  
  115.         for(i=0; i<BACKLOG; i++)  
  116.         {  
  117.             if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds))  
  118.             {  
  119.                 if(recvLen != REVLEN)  
  120.                 {  
  121.                     while(1)  
  122.                     {  
  123.                         //recv數據  
  124.                         ret = recv(client[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);  
  125.                         if(ret == 0)  
  126.                         {  
  127.                             client[i].fd = -1;  
  128.                             recvLen = 0;  
  129.                             break;  
  130.                         }  
  131.                         else if(ret < 0)  
  132.                         {  
  133.                             client[i].fd = -1;  
  134.                             recvLen = 0;  
  135.                             break;  
  136.                         }  
  137.                         //數據接受正常  
  138.                         recvLen = recvLen+ret;  
  139.                         if(recvLen<REVLEN)  
  140.                         {  
  141.                             continue;  
  142.                         }  
  143.                         else  
  144.                         {  
  145.                             //數據接受完畢  
  146.                             printf("%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf);  
  147.                             //close(client[i].fd);  
  148.                             //client[i].fd = -1;  
  149.                             recvLen = 0;  
  150.                             break;  
  151.                         }  
  152.                     }  
  153.                 }  
  154.             }  
  155.         }  
  156.       
  157.         //如果可讀  
  158.         if(FD_ISSET(sockListen, &readfds))  
  159.         {  
  160.             printf("isset\n");  
  161.             sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr  
  162.           
  163.             if(sockSvr == -1)  
  164.             {  
  165.                 printf("accpet error\n");  
  166.             }  
  167.             else  
  168.             {  
  169.                 currentClient++;  
  170.             }  
  171.           
  172.             for(i=0; i<BACKLOG; i++)  
  173.             {  
  174.                 if(client[i].fd < 0)  
  175.                 {  
  176.                     client[i].fd = sockSvr;  
  177.                     client[i].addr = client_addr;  
  178.                     printf("You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) );  
  179.                     break;  
  180.                 }  
  181.             }  
  182.             //close(sockListen);  
  183.         }  
  184.     }  
  185.   
  186.     printf("test\n");  
  187.     return 0;  
  188. }  
  189.   
  190. //顯示當前的connection  
  191. void showClient()  
  192. {  
  193.     int i;  
  194.     printf("client count = %d\n", currentClient);  
  195.   
  196.     for(i=0; i<BACKLOG; i++)  
  197.     {  
  198.         printf("[%d] = %d", i, client[i].fd);  
  199.     }  
  200.     printf("\n");  
  201. }  

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) 示例

  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.   
  13. typedef struct _CLIENT{  
  14.     int fd;  
  15.     struct sockaddr_in addr; /* client's address information */  
  16. } CLIENT;  
  17.   
  18. #define MYPORT 59000  
  19.   
  20. //最多處理的connect  
  21. #define BACKLOG 5  
  22.   
  23. //當前的連接數  
  24. int currentClient = 0;   
  25.   
  26. //數據接受 buf  
  27. #define REVLEN 10  
  28. char recvBuf[REVLEN];  
  29.   
  30. #define OPEN_MAX 1024  
  31.   
  32. int main()  
  33. {  
  34.     int i, ret, sinSize;  
  35.     int recvLen = 0;  
  36.     fd_set readfds, writefds;  
  37.     int sockListen, sockSvr, sockMax;  
  38.     int timeout;  
  39.     struct sockaddr_in server_addr;  
  40.     struct sockaddr_in client_addr;  
  41.   
  42.     struct pollfd clientfd[OPEN_MAX];  
  43.   
  44.   
  45.     //socket  
  46.     if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  47.     {  
  48.         printf("socket error\n");  
  49.         return -1;  
  50.     }  
  51.   
  52.     bzero(&server_addr, sizeof(server_addr));  
  53.     server_addr.sin_family  =  AF_INET;  
  54.     server_addr.sin_port = htons(MYPORT);  
  55.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  56.   
  57.     //bind  
  58.     if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  59.     {  
  60.         printf("bind error\n");  
  61.         return -1;  
  62.     }  
  63.   
  64.     //listen  
  65.     if(listen(sockListen, 5) < 0)  
  66.     {  
  67.         printf("listen error\n");  
  68.         return -1;  
  69.     }  
  70.   
  71.   
  72.     //clientfd 初始化  
  73.     clientfd[0].fd = sockListen;  
  74.     clientfd[0].events = POLLIN; //POLLRDNORM;  
  75.     sockMax = 0;  
  76.     for(i=1; i<OPEN_MAX; i++)  
  77.     {  
  78.         clientfd[i].fd = -1;  
  79.     }  
  80.   
  81.     //select  
  82.     while(1)  
  83.     {  
  84.         timeout=3000;                  
  85.         //select  
  86.         ret = poll(clientfd, sockMax+1, timeout);  
  87.       
  88.         if(ret < 0)  
  89.         {  
  90.             printf("select error\n");  
  91.             break;  
  92.         }  
  93.         else if(ret == 0)  
  94.         {  
  95.             printf("timeout ...\n");  
  96.             continue;  
  97.         }  
  98.       
  99.         if (clientfd[0].revents & POLLIN)//POLLRDNORM  
  100.         {  
  101.             sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr  
  102.           
  103.             if(sockSvr == -1)  
  104.             {  
  105.                 printf("accpet error\n");  
  106.             }  
  107.             else  
  108.             {  
  109.                 currentClient++;  
  110.             }  
  111.           
  112.             for(i=0; i<OPEN_MAX; i++)  
  113.             {  
  114.                 if(clientfd[i].fd<0)  
  115.                 {  
  116.                     clientfd[i].fd = sockSvr;  
  117.                     break;  
  118.                 }  
  119.             }  
  120.             if(i==OPEN_MAX)  
  121.             {  
  122.                 printf("too many connects\n");  
  123.                 return -1;  
  124.             }  
  125.             clientfd[i].events = POLLIN;//POLLRDNORM;  
  126.             if(i>sockMax)  
  127.                 sockMax = i;  
  128.         }  
  129.       
  130.         //讀取數據  
  131.         for(i=1; i<=sockMax; i++)  
  132.         {  
  133.             if(clientfd[i].fd < 0)  
  134.                 continue;  
  135.           
  136.             if (clientfd[i].revents & (POLLIN | POLLERR))//POLLRDNORM  
  137.             {  
  138.                 if(recvLen != REVLEN)  
  139.                 {  
  140.                     while(1)  
  141.                     {  
  142.                         //recv數據  
  143.                         ret = recv(clientfd[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);  
  144.                         if(ret == 0)  
  145.                         {  
  146.                             clientfd[i].fd = -1;  
  147.                             recvLen = 0;  
  148.                             break;  
  149.                         }  
  150.                         else if(ret < 0)  
  151.                         {  
  152.                             clientfd[i].fd = -1;  
  153.                             recvLen = 0;  
  154.                             break;  
  155.                         }  
  156.                         //數據接受正常  
  157.                         recvLen = recvLen+ret;  
  158.                         if(recvLen<REVLEN)  
  159.                         {  
  160.                             continue;  
  161.                         }  
  162.                         else  
  163.                         {  
  164.                             //數據接受完畢  
  165.                             printf("buf = %s\n",  recvBuf);  
  166.                             //close(client[i].fd);  
  167.                             //client[i].fd = -1;  
  168.                             recvLen = 0;  
  169.                             break;  
  170.                         }  
  171.                     }  
  172.                 }  
  173.             }  
  174.         }  
  175.     }  
  176.   
  177.     return 0;  
  178. }  

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.   
  14. typedef struct _CLIENT{  
  15.     int fd;  
  16.     struct sockaddr_in addr; /* client's address information */  
  17. } CLIENT;  
  18.   
  19. #define MYPORT 59000  
  20.   
  21. //最多處理的connect  
  22. #define MAX_EVENTS 500  
  23.   
  24. //當前的連接數  
  25. int currentClient = 0;   
  26.   
  27. //數據接受 buf  
  28. #define REVLEN 10  
  29. char recvBuf[REVLEN];  
  30.   
  31. //EPOLL相關   
  32. //epoll描述符  
  33. int epollfd;  
  34. //事件數組  
  35. struct epoll_event eventList[MAX_EVENTS];  
  36.   
  37. void AcceptConn(int srvfd);  
  38. void RecvData(int fd);  
  39.   
  40. int main()  
  41. {  
  42.     int i, ret, sinSize;  
  43.     int recvLen = 0;  
  44.     fd_set readfds, writefds;  
  45.     int sockListen, sockSvr, sockMax;  
  46.     int timeout;  
  47.     struct sockaddr_in server_addr;  
  48.     struct sockaddr_in client_addr;  
  49.       
  50.     //socket  
  51.     if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  52.     {  
  53.         printf("socket error\n");  
  54.         return -1;  
  55.     }  
  56.       
  57.     bzero(&server_addr, sizeof(server_addr));  
  58.     server_addr.sin_family  =  AF_INET;  
  59.     server_addr.sin_port = htons(MYPORT);  
  60.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);   
  61.       
  62.     //bind  
  63.     if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  64.     {  
  65.         printf("bind error\n");  
  66.         return -1;  
  67.     }  
  68.       
  69.     //listen  
  70.     if(listen(sockListen, 5) < 0)  
  71.     {  
  72.         printf("listen error\n");  
  73.         return -1;  
  74.     }  
  75.       
  76.     //1. epoll 初始化  
  77.     epollfd = epoll_create(MAX_EVENTS);  
  78.     struct epoll_event event;  
  79.     event.events = EPOLLIN|EPOLLET;  
  80.     event.data.fd = sockListen;  
  81.       
  82.     //2. epoll_ctrl  
  83.     if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0)  
  84.     {  
  85.         printf("epoll add fail : fd = %d\n", sockListen);  
  86.         return -1;  
  87.     }  
  88.       
  89.     //epoll  
  90.     while(1)  
  91.     {  
  92.         timeout=3000;                  
  93.         //3. epoll_wait  
  94.         int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout);  
  95.           
  96.         if(ret < 0)  
  97.         {  
  98.             printf("epoll error\n");  
  99.             break;  
  100.         }  
  101.         else if(ret == 0)  
  102.         {  
  103.             printf("timeout ...\n");  
  104.             continue;  
  105.         }  
  106.           
  107.         //直接獲取了事件數量,給出了活動的流,這裏是和poll區別的關鍵  
  108.         int n = 0;  
  109.         for(n=0; n<ret; n++)  
  110.         {  
  111.             //錯誤退出  
  112.             if ((eventList[n].events & EPOLLERR) ||  
  113.                 (eventList[n].events & EPOLLHUP) ||  
  114.                 !(eventList[n].events & EPOLLIN))  
  115.             {  
  116.               printf ( "epoll error\n");  
  117.               close (eventList[n].data.fd);  
  118.               return -1;  
  119.             }  
  120.               
  121.             if (eventList[n].data.fd == sockListen)  
  122.             {  
  123.                 AcceptConn(sockListen);  
  124.           
  125.             }else{  
  126.                 RecvData(eventList[n].data.fd);  
  127.                 //不刪除  
  128.              //   epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent);  
  129.             }  
  130.         }  
  131.     }  
  132.       
  133.     close(epollfd);  
  134.     close(sockListen);  
  135.       
  136.     printf("test\n");  
  137.     return 0;  
  138. }  
  139.   
  140. /************************************************** 
  141. 函數名:AcceptConn 
  142. 功能:接受客戶端的鏈接 
  143. 參數:srvfd:監聽SOCKET 
  144. ***************************************************/  
  145. void AcceptConn(int srvfd)  
  146. {  
  147.     struct sockaddr_in sin;  
  148.     socklen_t len = sizeof(struct sockaddr_in);  
  149.     bzero(&sin, len);  
  150.   
  151.     int confd = accept(srvfd, (struct sockaddr*)&sin, &len);  
  152.   
  153.     if (confd < 0)  
  154.     {  
  155.        printf("bad accept\n");  
  156.        return;  
  157.     }else  
  158.     {  
  159.         printf("Accept Connection: %d", confd);  
  160.     }  
  161.   
  162.     //setnonblocking(confd);  
  163.   
  164.     //4. epoll_wait  
  165.     //將新建立的連接添加到EPOLL的監聽中  
  166.     struct epoll_event event;  
  167.     event.data.fd = confd;  
  168.     event.events =  EPOLLIN|EPOLLET;  
  169.     epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event);  
  170. }  
  171.   
  172. //讀取數據  
  173. void RecvData(int fd)  
  174. {  
  175.     int ret;  
  176.     int recvLen = 0;  
  177.       
  178.     memset(recvBuf, 0, REVLEN);  
  179.     printf("RecvData function\n");  
  180.       
  181.     if(recvLen != REVLEN)  
  182.     {  
  183.         while(1)  
  184.         {  
  185.             //recv數據  
  186.             ret = recv(fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);  
  187.             if(ret == 0)  
  188.             {  
  189.                 recvLen = 0;  
  190.                 break;  
  191.             }  
  192.             else if(ret < 0)  
  193.             {  
  194.                 recvLen = 0;  
  195.                 break;  
  196.             }  
  197.             //數據接受正常  
  198.             recvLen = recvLen+ret;  
  199.             if(recvLen<REVLEN)  
  200.             {  
  201.                 continue;  
  202.             }  
  203.             else  
  204.             {  
  205.                 //數據接受完畢  
  206.                 printf("buf = %s\n",  recvBuf);  
  207.                 recvLen = 0;  
  208.                 break;  
  209.             }  
  210.         }  
  211.     }  
  212.   
  213.     printf("content is %s", recvBuf);  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章