当前位置: 首页 > 工具软件 > xPool > 使用案例 >

select,xpool,cpool,epool

王翰墨
2023-12-01

cpool,xpool, epool,apool

  • xpool: 是很经典的领导者追随者模型,因为不想命名太长,就叫xpool。多个工作线程同时accept竞争一个可用的连接,拿到连接后就自己进行处理。accept这个地方加了锁是为了避免低版本内核上出现惊群效应. 一般认为在短连接的时候效果比较好,但如果同一时候连接数过多会造成没有工作线程与客户端进行连接,客户端会出现大量的连接失败。

  • cpool: 经典的生产者消费者模型,由一个线程不断的建立连接,通过使用select判断是否可读,可读放入等待队列,多个工作线程从等待队列里获取连接进行处理。在大压力下很少出现像xpool那样出现连接失败的问题。cpool的一个经典问题就是在长连接的时候的性能问题,如果长连接的连接数比工作线程还少的时候,当所有的连接都被处理了,有连接需要放回pool中,而这个时候如果正常建立连接的监听线程正好处于select状态,这个时候必须要等到select超时才能重新将连接放入select中进行监听,因为这之前被放入select进行监听的处理socket为空,不会有响应,这个时候由于时间的浪费造成cpool长连接的性能下降。过去的一些做法是控制连接数和服务端的工作线程数以及通过监听一个管道fd,在工作线程结束每次都激活这个fd跳出这次select。现在可以简单换成epool就可以避免这个问题

  • epool: 这个只在2.6内核上支持,我们可以在2.4内核上编译到2.6内核上运行。epool的模型与cpool是一样的,只是使用epoll代替了select。提高了性能。另外由于epoll中监听的socket是随时放入,随时可以被监听到,所以不会出现上面cpool中的问题。

select epoll的区别

select epoll都是用来监听套接字上是否有事件发生,简单来讲,select是轮询方式,而epoll是触发方式的,用回调把信息赋给event结构体。

select:轮询检查文件描述符集合,实现方法如下:

fd_set    fdRead;

//将文件描述符集合清零

FD_ZERO(&fdRead);

//在文件描述rd中增加文件描述符iSocket,可以加多个

FD_SET(iSocket,&fdRead);

//select ,设置recv超时时间,写的类似

int iRet = select(iSocket+ 1, &fdRead, NULL, NULL, &tvTimeOut);

switch(iRet)

{

       case 0:

              printf("Time out \n");

              return -1;

       case -1:

              printf("Select Error\n");

              return -2;

       default:

              if (FD_ISSET(iSocket,&fdRead))//测试iSocket是否在描述符集合中

              {

                     iRecvLen = recv(iSocket,pcRecvBuf, iBufLen, 0);

                     if (iRecvLen == -1)

                     {

                            printf("Recverror\n");

                            return -3;

                     }

                     else if(0 == iRecvLen)

                     {

                            printf("Socketclose \n");

                            return -4;

                     }

                     else

                     {

                            break;

                     }

              }

}

如果文件描述符中有多个socket,便会轮询检查。

 

epoll:epoll的使用,首先通过epoll_create创建一个epoll对象,得到一个epoll的描述符,我们需要监听哪个fd上的事件通过epoll_ctl,最后 epoll_wait会返回发生了事件的数目,具体的事件在参数events中。

#include<stdio.h>

#include<sys/socket.h>

#include<sys/types.h>

#include<unistd.h>

#include<netinet/in.h>

#include<stdlib.h>

#include<sys/ioctl.h>

#include<sys/epoll.h>

 

#define MAX_EVENTS10

#define  LISTENQ    12

 

int main()

{

       int n = 0;

       int listen_sock;

       int conn_sock;

       int nfds;

       int epollfd;

       int iRet = -1;

       socklen_t len;

       struct epoll_event ev;

       struct epoll_event events[MAX_EVENTS];

       char buf[32] = "tongzhilin";

 

       /*create listen socket */

       listen_sock = socket(AF_INET,SOCK_STREAM, 0);

       int   iSocketOpt= 1;

       setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,(void *)&iSocketOpt, sizeof(int));

 

       /*bind*/

       struct     sockaddr_instServerAddrIn;

       stServerAddrIn.sin_family = AF_INET;

       stServerAddrIn.sin_port = htons(6666);

       stServerAddrIn.sin_addr.s_addr =htonl(INADDR_ANY);

       iRet = bind(listen_sock,(structsockaddr*)&stServerAddrIn, sizeof(stServerAddrIn));

       if(iRet < 0)

       {

              printf("bind error\n");

              return -3;

       }

 

       /*listen*/

       iRet = listen(listen_sock, LISTENQ);

       if(iRet < 0)

       {

              printf("listenerror\n");

              return -3;

       }

 

       /*epoll*/

       epollfd = epoll_create(10);

       if(epollfd == -1)

       {

              perror("epoll_create");

              return -1;

       }

      

       ev.events = EPOLLIN;

       ev.data.fd = listen_sock;

 

       //EPOLL_CTL_ADD:注册;EPOLL_CTL_MOD修改;EPOLL_CTL_DEL:删除

       if(epoll_ctl(epollfd, EPOLL_CTL_ADD,listen_sock, &ev) == -1)

       {

              perror("epoll_ctl:listen_sock");

              return -1;

       }

 

       while(1)

       {

              printf("Wait..\n");

              nfds = epoll_wait(epollfd, events,MAX_EVENTS, -1);

              if(nfds == -1)

              {

                     perror("epoll_pwait");

                     return -1;

              }

 

              for (n = 0; n < nfds; ++n)

              {

                     if(events[n].data.fd ==listen_sock)

                     {

                            conn_sock =accept(listen_sock, (struct sockaddr *)&stServerAddrIn, &len);

                            if(conn_sock == -1)

                            {

                                   perror("accept");

                                   return -1;

                            }

                           

                            //非阻塞

                            int ulflag = 1;

                            ioctl(listen_sock,FIONBIO, &ulflag);

                            ev.events = EPOLLIN| EPOLLET;

                            ev.data.fd =conn_sock;

                            //EPOLL_CTL_ADD:注册;EPOLL_CTL_MOD修改;EPOLL_CTL_DEL:删除

                            if(epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1)

                            {

                                   perror("epoll_ctl:conn_sock");

                                   return -1;

                            }

                    }

                     else

                     {

                            send(events[n].data.fd,buf, sizeof(buf), 0);

                     }

               }

       }

      

       return 0;

}

总结:select如果同时建立的连接很多,但是只有少数的有事件发生,这种情况下,效率就会很低。epoll的实现中就避免了该问题,对于要监管的每个fd都会有回调函数,当该fd上发生事件时,会调用对应的回调函数。这样,在连接很多,少数事件发生的情况下,依旧会效率很高。当然,如果连接很多,大部分连接都有事件时,两者的效率应该是差不多的。


 类似资料: