之前的例子是使用的简单的socket编程,这样可以实现简单的一对一的连接,如果我们的服务端可能有很多的客户端呢?此时该怎么办?
     这里我们用使用epoll,为什么是epoll?epoll是在linux2.6中添加的。
     它有三个特性:mmap,红黑树,双链表。这三个特性使得它的性能优于select,poll。
      mmap将用户空间和内核空间映射到同一块内存中,使得数据传递不再是select,poll中使用的句柄拷贝,而是直接访问就可以了。
      先介绍下它的函数:

点击(此处)折叠或打开

  1. #include <sys/epoll.h>
  2. int epoll_create(int size);
  3. int epoll_create1(int flags)
  4. //Since Linux 2.6.8, the size argument is ignored, but must be greater than  zero; see NOTES below.
作为函数返回值,epoll_create()返回了代表新创建的epoll 实例的文件描述符。这个文件描述符在其他几个epoll 系统调用中用来表示epoll 实例。
当这个文件描述符不再需要时,应该通过close()来关闭。当所有与epoll 实例相关的文件描述符都被关闭时,实例被销毁,相关的资源都返还给系统。
(多个文件描述符可能引用到相同的epoll 实例,这是由于调用了fork()或者dup()这样类似的函数所致。)

点击(此处)折叠或打开

  1. #include <sys/epoll.h>
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epfd:
修改由文件描述符epfd 所代表的epoll 实例中的兴趣列表,

int op
参数op 用来指定需要执行的操作,它可以是如下几种值:
EPOLL_CTL_ADD
将描述符fd 添加到epoll 实例epfd 中的兴趣列表中去。对于fd 上我们感兴趣的事件,都
指定在ev 所指向的结构体中,下面会详细介绍。如果我们试图向兴趣列表中添加一个已存在
的文件描述符,epoll_ctl()将出现EEXIST 错误。

EPOLL_CTL_MOD
修改描述符fd 上设定的事件,需要用到由ev 所指向的结构体中的信息。如果我们试图修
改不在兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT 错误。

EPOLL_CTL_DEL
将文件描述符fd 从epfd 的兴趣列表中移除。该操作忽略参数ev。如果我们试图移除一个
不在epfd 的兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT 错误。关闭一个文件描述
符会自动将其从所有的epoll 实例的兴趣列表中移除。

int fd
指明了要修改兴趣列表中的哪一个文件描述符的设定。该参数可以是代表管道、
FIFO、套接字、POSIX 消息队列、inotify 实例、终端、设备,甚至是另一个epoll 实例的文件
描述符(例如,我们可以为受检查的描述符建立起一种层次关系)。但是,这里fd 不能作为普
通文件或目录的文件描述符(会出现EPERM 错误)。

struct epoll_event *event:
参数event 是指向结构体epoll_event 的指针,结构体的定义如下。

点击(此处)折叠或打开

  1. struct epoll_event
  2. {
  3.   uint32_t events;
  4.   epoll_data_t data;
  5. };
结构体epoll_event 中的data 字段的类型为:

点击(此处)折叠或打开

  1. typedef union epoll_data
  2. {
  3.    void *ptr;
  4.    int fd;
  5.    uint32_t u32;
  6.    uint64_t u64;
  7. }epoll_data_t;
参数ev 为文件描述符fd 所做的设置如下:
    1.结构体 epoll_event 中的 events 字段是一个位掩码,它指定了我们为待检查的描述
符 fd 上所感兴趣的事件集合。
socket通信进阶一:使用epoll。-LMLPHP

    2.data 字段是一个联合体,当描述符fd 稍后成为就绪态时,联合体的成员可用来指定传
回给调用进程的信息。

int epoll_wait(int epfd, struct epoll_event *evlist,int maxevents, int timeout);
struct epoll_event *evlist,int maxevents:
参数evlist 所指向的结构体数组中返回的是有关就绪态文件描述符的信息。(结构体epoll_event 已经在上一节中描述。)数组evlist 的空间由调用者负责申请,所包含的元素个数在参数maxevents 中指定。

在数组evlist中,每个元素返回的都是单个就绪态文件描述符的信息。events 字段返回了在该描述符上已经发生的事件掩码。
data 字段返回的是我们在描述符上使用 epoll_ctl()注册感兴趣的事件时在ev.data 中所指定的值。
注意,data 字段是唯一可获知同这个事件相关的文件描述符号的途径。因此,当我们调用epoll_ctl()将文件描述符添加到兴趣列表中时,应该要么将ev.data.fd 设为文件描述符号,要么将ev.data.ptr 设为指向包含文件描述符号的结构体。
 
int timeout:
参数timeout 用来确定epoll_wait()的阻塞行为,有如下几种。
   如果timeout 等于?1,调用将一直阻塞,直到兴趣列表中的文件描述符上有事件产生,或者直到捕获到一个信号为止。
   如果timeout 等于0,执行一次非阻塞式的检查,看兴趣列表中的文件描述符上产生了哪个事件。
   如果timeout 大于0,调用将阻塞至多timeout 毫秒,直到文件描述符上有事件发生,或者直到捕获到一个信号为止。
使用epoll的实例,功能与上篇相同,还是求直角三角形斜边:

点击(此处)折叠或打开

  1. #include <cstdio>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <arpa/inet.h>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <unistd.h>
  8. #include <netinet/in.h>
  9. #include <sys/epoll.h>
  10. #include <cerrno>
  11. #include <iostream>
  12. #include <string>
  13. #include <vector>
  14. #include <cmath>
  15. #include <sys/resource.h>    


  16. using namespace std;

  17. #define    MAXEPOLL    10000
  18. #define    MAXLINE        1024
  19. #define MAX_MSG_SIZE 256
  20. #define SERVER_PORT 9987
  21.  
  22.  
  23. #define BACKLOG 2

  24. struct msg_req{
  25.     int len;
  26.     char msg[MAX_MSG_SIZE];
  27. };

  28. struct msg_rsp{
  29.   int len;
  30.   char msg[MAX_MSG_SIZE];
  31. };
  32.  
  33. vector<string> split(string strtem,char a)
  34. {
  35.     vector<string> strvec;
  36.     string::size_type pos1, pos2;
  37.     pos2 = strtem.find(a);
  38.     pos1 = 0;
  39.     while (string::npos != pos2)
  40.     {
  41.         strvec.push_back(strtem.substr(pos1, pos2 - pos1));
  42.         pos1 = pos2 + 1;
  43.         pos2 = strtem.find(a, pos1);
  44.     }
  45.     strvec.push_back(strtem.substr(pos1));
  46.     return strvec;
  47. }

  48. void process(struct msg_req &req,struct msg_rsp &rsp)
  49. {
  50.   int len=strlen(req.msg);
  51.   printf("len=%d,msg=%s\n",len,req.msg);
  52.   
  53.   std::string str_cmd(req.msg);
  54.   vector<string> res_list=split(str_cmd,',');
  55.   float a=atof(res_list[0].c_str());
  56.   float b=atof(res_list[1].c_str());
  57.   float c=sqrt((pow(a,2))+(pow(b,2)));
  58.   string str_result=std::to_string(c);
  59.   strcpy(rsp.msg,str_result.c_str());
  60.   return;
  61. }
  62.  
  63. int main(){
  64.     int         conn_fd;
  65.     int         epoll_fd;
  66.     int         nread;
  67.     int         cur_fds;
  68.     int         wait_fds;
  69.     
  70.     int sock_fd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */
  71.     struct sockaddr_in ser_addr; /* 本机地址信息 */
  72.     struct sockaddr_in cli_addr; /* 客户端地址信息 */
  73.     char msg[MAX_MSG_SIZE];/* 缓冲区*/
  74.     int ser_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */
  75.     if(ser_sockfd<0)
  76.     {
  77.         /*创建失败 */
  78.         fprintf(stderr,"socker Error:%s\n",strerror(errno));
  79.         exit(1);
  80.     }
  81.     struct     epoll_event    ev;
  82.     struct     epoll_event    evs[MAXEPOLL];
  83.     struct     rlimit    rlt;
  84.     rlt.rlim_max = rlt.rlim_cur = MAXEPOLL;

  85.     /* 初始化服务器地址*/
  86.     socklen_t addrlen=sizeof(struct sockaddr_in);
  87.     bzero(&ser_addr,addrlen);
  88.     ser_addr.sin_family=AF_INET;
  89.     ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  90.     ser_addr.sin_port=htons(SERVER_PORT);
  91.     if(bind(ser_sockfd,(struct sockaddr*)&ser_addr,sizeof(struct sockaddr_in))<0)
  92.     { /*绑定失败 */
  93.         fprintf(stderr,"Bind Error:%s\n",strerror(errno));
  94.         exit(1);
  95.     }

  96.     
  97.     /*侦听客户端请求*/
  98.     if(listen(ser_sockfd,BACKLOG)<0)
  99.     {
  100.         fprintf(stderr,"Listen Error:%s\n",strerror(errno));
  101.         close(ser_sockfd);
  102.         exit(1);
  103.     }


  104.     epoll_fd = epoll_create( MAXEPOLL ); //
  105.     ev.events = EPOLLIN | EPOLLET;    //表示对应的文件描述符可以读|采用边缘触发事件通知
  106.     ev.data.fd = ser_sockfd;

  107.     if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, ser_sockfd, &ev ) < 0 )
  108.     {
  109.         printf("Epoll Error : %d\n", errno);
  110.         exit( EXIT_FAILURE );
  111.     }
  112.     
  113.     cur_fds = 1;
  114.     struct msg_req req;
  115.     struct msg_rsp rsp;
  116.     while(1)
  117.     {
  118.      if( ( wait_fds = epoll_wait( epoll_fd, evs, cur_fds, -1 ) ) == -1 ) //
  119.      {
  120.             printf( "Epoll Wait Error : %d\n", errno );
  121.             exit( -1 );
  122.         }
  123.         for( i = 0; i < wait_fds; i++ )
  124.         {
  125.             if( evs[i].data.fd == ser_sockfd && cur_fds < MAXEPOLL )
  126.             {
  127.               if( ( conn_fd = accept( ser_sockfd, (struct sockaddr *)&cli_addr, &addrlen ) ) == -1 )
  128.               {
  129.                     printf("Accept Error : %d\n", errno);
  130.                     exit( -2 );
  131.              }

  132.              printf( "Server get from client !\n"/*, inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port */);

  133.              ev.events = EPOLLIN | EPOLLET;
  134.              ev.data.fd = conn_fd;
  135.              if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev ) < 0 )//将accept加入监听对列
  136.              {
  137.                  printf("Epoll Error : %d\n", errno);
  138.                  exit( -3 );
  139.              }

  140.              ++cur_fds;
  141.              continue;
  142.    
  143.             }
  144.            
  145.             
  146.             req.msg[0]='\0';
  147.             rsp.msg[0]='\0';
  148.             nread = read( evs[i].data.fd, req.msg, sizeof(msg) );
  149.             
  150.             printf("recv:%s\n",req.msg);
  151.             if( nread <= 0 )
  152.             {
  153.                 close( evs[i].data.fd );
  154.                 epoll_ctl( epoll_fd, EPOLL_CTL_DEL, evs[i].data.fd, &ev );//将accept删除
  155.                 --cur_fds;
  156.                 continue;
  157.             }

  158.             process(req,rsp);
  159.             printf("send:%s\n",rsp.msg);
  160.             write( evs[i].data.fd, rsp.msg, nread );

  161.         }

  162.         
  163.     
  164.     }

  165.     close(ser_sockfd);
  166.     return 0;
  167.  }

10-17 13:27