Linux select/epoll网络模型

select,epoll网络模型经常在面试中出现,epoll是对poll的优化,是linux下最优秀的网络模型

epoll优点:

# 相对select,没有最大并发数限制 /proc/sys/file-max

# 数据传递(用户空间跟内核空间)通过共享内存(mmap)方式

# epoll_wait 直接返回被触发的fd对应的一块buffer,不需要遍历所有的fd

 

一.Linux select模型

流程:

1. 声明数组fd_A,添加多个socket client fd

2. 监听端口

3. 将sock_fd 和 数组fd不为0描述符放入select将检查的fd_set中

4. 处理 fdsr可以接收数据的连接; 如是sock_fd,添加新连接至fd_A;  

详见: http://blog.chinaunix.net/uid-25808509-id-2233262.html

 

C代码   收藏代码
  1. // select_tcp_server.c     
  2. #include <stdio.h>    
  3. #include <stdlib.h>    
  4. #include <unistd.h>    
  5. #include <errno.h>    
  6. #include <string.h>    
  7. #include <sys/types.h>    
  8. #include <sys/socket.h>    
  9. #include <netinet/in.h>    
  10. #include <arpa/inet.h>    
  11.     
  12. #define MYPORT 1234    // the port users will be connecting to    
  13. #define BACKLOG 5     // how many pending connections queue will hold    
  14. #define BUF_SIZE 200    
  15.     
  16. int fd_A[BACKLOG];    // accepted connection fd    
  17. int conn_amount;    // current connection amount    
  18.     
  19. void showclient()    
  20. {    
  21.     int i;    
  22.     printf("client amount: %d\n", conn_amount);    
  23.     for (i = 0; i < BACKLOG; i++) {    
  24.         printf("[%d]:%d ", i, fd_A[i]);    
  25.     }    
  26.     printf("\n\n");    
  27. }    
  28.     
  29. int main(void)    
  30. {    
  31.     int sock_fd, new_fd;  // listen on sock_fd, new connection on new_fd    
  32.     struct sockaddr_in server_addr;    // server address information    
  33.     struct sockaddr_in client_addr; // connector's address information    
  34.     socklen_t sin_size;    
  35.     int yes = 1;    
  36.     char buf[BUF_SIZE];    
  37.     int ret;    
  38.     int i;    
  39.     
  40.     if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {    
  41.         perror("socket");    
  42.         exit(1);    
  43.     }    
  44.     
  45.     if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {    
  46.         perror("setsockopt");    
  47.         exit(1);    
  48.     }    
  49.         
  50.     server_addr.sin_family = AF_INET;         // host byte order    
  51.     server_addr.sin_port = htons(MYPORT);     // short, network byte order    
  52.     server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP    
  53.     memset(server_addr.sin_zero, '\0'sizeof(server_addr.sin_zero));    
  54.     
  55.     if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {    
  56.         perror("bind");    
  57.         exit(1);    
  58.     }    
  59.     
  60.     if (listen(sock_fd, BACKLOG) == -1) {    
  61.         perror("listen");    
  62.         exit(1);    
  63.     }    
  64.     
  65.     printf("listen port %d\n", MYPORT);    
  66.     
  67.     fd_set fdsr;    
  68.     int maxsock;    
  69.     struct timeval tv;    
  70.     
  71.     conn_amount = 0;    
  72.     sin_size = sizeof(client_addr);    
  73.     maxsock = sock_fd;    
  74.     while (1) {    
  75.         // initialize file descriptor set    
  76.         FD_ZERO(&fdsr);    
  77.         FD_SET(sock_fd, &fdsr);    
  78.     
  79.         // timeout setting    
  80.         tv.tv_sec = 30;    
  81.         tv.tv_usec = 0;    
  82.     
  83.         // add active connection to fd set    
  84.         for (i = 0; i < BACKLOG; i++) {    
  85.             if (fd_A[i] != 0) {    
  86.                 FD_SET(fd_A[i], &fdsr);    
  87.             }    
  88.         }    
  89.     
  90.         ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);    
  91.         if (ret < 0) {    
  92.             perror("select");    
  93.             break;    
  94.         } else if (ret == 0) {    
  95.             printf("timeout\n");    
  96.             continue;    
  97.         }    
  98.     
  99.         // check every fd in the set    
  100.         for (i = 0; i < conn_amount; i++) {    
  101.             if (FD_ISSET(fd_A[i], &fdsr)) {    
  102.                 ret = recv(fd_A[i], buf, sizeof(buf), 0);    
  103.                 if (ret <= 0) {        // client close    
  104.                     printf("client[%d] close\n", i);    
  105.                     close(fd_A[i]);    
  106.                     FD_CLR(fd_A[i], &fdsr);    
  107.                     fd_A[i] = 0;    
  108.                 } else {        // receive data    
  109.                     if (ret < BUF_SIZE)    
  110.                         memset(&buf[ret], '\0', 1);    
  111.                     printf("client[%d] send:%s\n", i, buf);    
  112.                 }    
  113.             }    
  114.         }    
  115.     
  116.         // check whether a new connection comes    
  117.         if (FD_ISSET(sock_fd, &fdsr)) {    
  118.             new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);    
  119.             if (new_fd <= 0) {    
  120.                 perror("accept");    
  121.                 continue;    
  122.             }    
  123.     
  124.             // add to fd queue    
  125.             if (conn_amount < BACKLOG) {    
  126.                 fd_A[conn_amount++] = new_fd;    
  127.                 printf("new connection client[%d] %s:%d\n", conn_amount,    
  128.                         inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));    
  129.                 if (new_fd > maxsock)    
  130.                     maxsock = new_fd;    
  131.             }    
  132.             else {    
  133.                 printf("max connections arrive, exit\n");    
  134.                 send(new_fd, "bye", 4, 0);    
  135.                 close(new_fd);    
  136.                 break;    
  137.             }    
  138.         }    
  139.     
  140.         showclient();    
  141.     }    
  142.     
  143.     // close other connections    
  144.     for (i = 0; i < BACKLOG; i++) {    
  145.         if (fd_A[i] != 0) {    
  146.             close(fd_A[i]);    
  147.         }    
  148.     }    
  149.     
  150.     exit(0);    
  151. }  

 

二. linux epoll模型

C代码   收藏代码
  1. //epoll_tcp_server.c  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <netinet/in.h>  
  5. #include <sys/socket.h>  
  6. #include <sys/epoll.h>  
  7. #include <arpa/inet.h>  
  8. #include <fcntl.h>  
  9. #include <errno.h>  
  10. #include <time.h>  
  11. #include <list>  
  12. #include <stdlib.h>  
  13. #include <string.h>  
  14.   
  15.   
  16. #define BUF_SIZE 1024  
  17. #define SERV_PORT 1234  
  18. #define EPOLL_RUN_TIMEOUT -1  
  19. #define EPOLL_SIZE 10000  
  20.   
  21. #define STR_WELCOME "welcome to seChat! You ID is:Client #%d"  
  22. #define STR_MESSAGE "Client #%d>>%s"  
  23. #define STR_NOONE_CONNECTED "Noone connected to server except you"  
  24.   
  25. #define CHK(eval) if(eval<0){ perror("eval"); exit(-1); }  
  26. #define CHK2(res,eval) if((res = eval)<0){ perror("eval"); exit(-1); }  
  27.   
  28.   
  29. using namespace std;  
  30.   
  31. list<int> clients_list;  
  32.   
  33.   
  34. // 设置非阻塞  
  35. int SetNoblocking(int sockfd)  
  36. {  
  37.     CHK(fcntl(sockfd,F_SETFL,fcntl(sockfd,F_GETFD,0)|O_NONBLOCK));  
  38.     return 0;  
  39. }  
  40.   
  41.   
  42. // 处理消息  
  43. int HandleMsg(int client)  
  44. {  
  45.     char buf[BUF_SIZE],message[BUF_SIZE];  
  46.     bzero(buf,BUF_SIZE);  
  47.     bzero(message,BUF_SIZE);  
  48.   
  49.     int len;  
  50.     CHK2(len,recv(client,buf,BUF_SIZE,0));  
  51.     printf("Accept:%s\n",buf);  
  52.   
  53.     // 客户端关闭  
  54.     if(len==0)  
  55.     {  
  56.         CHK(close(client));  
  57.         clients_list.remove(client);  
  58.     }else  
  59.     {  
  60.         // 向客户端发送消息  
  61.         if(clients_list.size()==1)  
  62.         {  
  63.             CHK(send(client,STR_NOONE_CONNECTED,strlen(STR_NOONE_CONNECTED),0));  
  64.             return len;  
  65.         }  
  66.   
  67.         sprintf(message,STR_MESSAGE,client,buf);  
  68.   
  69.         list<int>::iterator it;  
  70.         for(it = clients_list.begin(); it!=clients_list.end();it++)  
  71.         {  
  72.             if(*it!=client)  
  73.             {  
  74.                 CHK(send(*it,message,BUF_SIZE,0));  
  75.             }  
  76.         }  
  77.     }  
  78.   
  79.     return len;  
  80. }  
  81.   
  82.   
  83. int main(int argc,char **argv)  
  84. {  
  85.     int listener;  
  86.     struct sockaddr_in addr,their_addr;  
  87.     addr.sin_family = AF_INET;  
  88.     addr.sin_port = htons(SERV_PORT);  
  89.     addr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  90.     socklen_t socklen;  
  91.     socklen = sizeof(struct sockaddr_in);  
  92.   
  93.     static struct epoll_event ev,events[EPOLL_SIZE];  
  94.     ev.events = EPOLLIN|EPOLLET; // 读感兴趣,边沿触发   
  95.   
  96.     char message[BUF_SIZE];  
  97.     int epfd;  
  98.     clock_t tStart;  
  99.     int client,res,epoll_events_count;  
  100.   
  101.     CHK2(listener,socket(AF_INET,SOCK_STREAM,0));  
  102.     SetNoblocking(listener);  
  103.   
  104.     CHK(bind(listener,(struct sockaddr*)&addr,sizeof(addr)));  
  105.     CHK(listen(listener,1));  
  106.   
  107.     CHK2(epfd,epoll_create(EPOLL_SIZE));  
  108.     ev.data.fd = listener;  
  109.     CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,listener,&ev));  
  110.   
  111.     while(1)  
  112.     {  
  113.         CHK2(epoll_events_count,epoll_wait(epfd,events,EPOLL_SIZE,EPOLL_RUN_TIMEOUT));  
  114.   
  115.         tStart = clock();  
  116.   
  117.         int i;  
  118.         for(i=0;i<epoll_events_count;i++)  
  119.         {  
  120.             if(events[i].data.fd == listener) // new connection  
  121.             {  
  122.                 CHK2(client,accept(listener,(struct sockaddr*)&their_addr,&socklen));  
  123.   
  124.                 SetNoblocking(client);  
  125.                 ev.data.fd = client;  
  126.   
  127.                 CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,client,&ev)); // register  
  128.   
  129.                 clients_list.push_back(client); // add to list  
  130.   
  131.                 bzero(message,BUF_SIZE);  
  132.                 res = sprintf(message,STR_WELCOME,client);  
  133.                 CHK2(res,send(client,message,BUF_SIZE,0));  
  134.             }else  
  135.             {  
  136.                 CHK2(res,HandleMsg(events[i].data.fd));  
  137.             }  
  138.   
  139.             printf("Statistics: %d events handled at: %.2fs\n",epoll_events_count,(double)(clock()-tStart)/CLOCKS_PER_SEC); }  
  140.     }  
  141.   
  142.     close(listener);  
  143.     close(epfd);  
  144.   
  145.     return 0;  
  146. }  

 

    TCP测试客户端,模拟1万个连接

C代码   收藏代码
  1. // huge_tcp_client.c   
  2. #define EPOLL_SIZE 10000  
  3. using namespace std;  
  4.   
  5. char message[BUF_SIZE];  
  6. list<int> list_of_clients;  
  7. int res;  
  8. clock_t tStart;  
  9.   
  10. int main(int argc,char **argv)  
  11. {  
  12.     int sock;  
  13.     struct sockaddr_in addr;  
  14.     addr.sin_family = AF_INET;  
  15.     addr.sin_port = htons(SERV_PORT);  
  16.     addr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  17.   
  18.     tStart = clock();  
  19.   
  20.     for(int i=0;i<EPOLL_SIZE;i++)  
  21.     {  
  22.         CHK2(sock,socket(AF_INET,SOCK_STREAM,0));  
  23.         CHK(connect(sock,(struct sockaddr*)&addr,sizeof(addr))<0);  
  24.         list_of_clients.push_back(sock);  
  25.   
  26.         bzero(&message,BUF_SIZE);  
  27.         CHK2(res,recv(sock,message,BUF_SIZE,0));  
  28.         printf("Accept:%s\n",message);    
  29.     }  
  30.   
  31.     list<int>::iterator it;  
  32.     for(it = list_of_clients.begin();it!=list_of_clients.end();it++)  
  33.     {  
  34.         close(*it);  
  35.     }  
  36.   
  37.     printf("Test passed:.2fs\n",(double)(clock()-tStart)/CLOCKS_PER_SEC);  
  38.     printf("Total server connections:%d\n",EPOLL_SIZE);  
  39.   
  40.     return 0;  
  41. }  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值