select和poll没有本质区别,poll不限制客户端的个数,两个函数的用法不一样

select用到的是

       FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空 处理,所以结果是不可知的。

  FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。

  FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。

  FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。

  需要两个变量来辅助,一个用来存储标识符 allset(因为每次select之后,存储的标识符都会变),一个(client[])用来标记连接符的位置

 1 for ( ; ; ) 
 2 {
 3     rset = allset;             /* 每次循环时都从新设置select监控信号集 */
 4     nready = select(maxfd+1, &rset, NULL, NULL, NULL);
 5 
 6     if (nready < 0)
 7         perr_exit("select error");
 8     if (FD_ISSET(listenfd, &rset)) { /* new client connection */
 9         cliaddr_len = sizeof(cliaddr);
10         connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
11         printf("received from %s at PORT %d\n",
12                 inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
13                 ntohs(cliaddr.sin_port));
14         for (i = 0; i < FD_SETSIZE; i++) {
15             if (client[i] < 0) {
16                 client[i] = connfd; /* 保存accept返回的文件描述符到client[]里 */
17                 break;
18             }
19         }
20         /* 达到select能监控的文件个数上限 1024 */
21         if (i == FD_SETSIZE) {
22             fputs("too many clients\n", stderr);
23             exit(1);
24         }
25 
26         FD_SET(connfd, &allset);     /* 添加一个新的文件描述符到监控信号集里 */
27         if (connfd > maxfd)
28             maxfd = connfd;         /* select第一个参数需要 */
29         if (i > maxi)
30             maxi = i;                 /* 更新client[]最大下标值 */
31 
32         if (--nready == 0)
33             continue;                 /* 如果没有更多的就绪文件描述符继续回到上面select阻塞监听,
34                                         负责处理未处理完的就绪文件描述符 */
35         }
36     for (i = 0; i <= maxi; i++) {     /* 检测哪个clients 有数据就绪 */
37         if ( (sockfd = client[i]) < 0)
38                 continue;
39         if (FD_ISSET(sockfd, &rset)) {
40             if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
41             Close(sockfd);        /* 当client关闭链接时,服务器端也关闭对应链接 */
42             FD_CLR(sockfd, &allset); /* 解除select监控此文件描述符 */
43             client[i] = -1;
44             } else {
45                 int j;
46                 for (j = 0; j < n; j++)
47                     buf[j] = toupper(buf[j]);
48                 Write(sockfd, buf, n);
49                 }
50             if (--nready == 0)
51                 break;
52         }
53     }
54 }
View Code

 

poll不需要额外的增加辅助变量来提高效率

struct pollfd 
{
    int fd; /* 文件描述符 */
    short events; /* 监控的事件 */
    short revents; /* 监控事件中满足条件返回的事件 */
};
    POLLIN            普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
    POLLRDNORM        数据可读
    POLLRDBAND        优先级带数据可读
    POLLPRI         高优先级可读数据
    POLLOUT            普通或带外数据可写
    POLLWRNORM        数据可写
    POLLWRBAND        优先级带数据可写
    POLLERR         发生错误
    POLLHUP         发生挂起
    POLLNVAL         描述字不是一个打开的文件
View Code
    for (i = 1; i < OPEN_MAX; i++)
        client[i].fd = -1;                             /* 用-1初始化client[]里剩下元素 */
    maxi = 0;                                         /* client[]数组有效元素中最大元素下标 */

    for ( ; ; ) {
        nready = poll(client, maxi+1, -1);             /* 阻塞 */
        if (client[0].revents & POLLRDNORM) {         /* 有客户端链接请求 */
            clilen = sizeof(cliaddr);
            connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
            printf("received from %s at PORT %d\n",
                    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                    ntohs(cliaddr.sin_port));
            for (i = 1; i < OPEN_MAX; i++) {
                if (client[i].fd < 0) {
                    client[i].fd = connfd;     /* 找到client[]中空闲的位置,存放accept返回的connfd */
                    break;
                }
            }

            if (i == OPEN_MAX)
                perr_exit("too many clients");

            client[i].events = POLLRDNORM;         /* 设置刚刚返回的connfd,监控读事件 */
            if (i > maxi)
                maxi = i;                         /* 更新client[]中最大元素下标 */
            if (--nready <= 0)
                continue;                         /* 没有更多就绪事件时,继续回到poll阻塞 */
        }
        for (i = 1; i <= maxi; i++) {             /* 检测client[] */
            if ((sockfd = client[i].fd) < 0)
                continue;
            if (client[i].revents & (POLLRDNORM | POLLERR)) {
                if ((n = Read(sockfd, buf, MAXLINE)) < 0) {
                    if (errno == ECONNRESET) { /* 当收到 RST标志时 */
                        /* connection reset by client */
                        printf("client[%d] aborted connection\n", i);
                        Close(sockfd);
                        client[i].fd = -1;
                    } else {
                        perr_exit("read error");
                    }
                } else if (n == 0) {
                    /* connection closed by client */
                    printf("client[%d] closed connection\n", i);
                    Close(sockfd);
                    client[i].fd = -1;
                } else {
                    for (j = 0; j < n; j++)
                        buf[j] = toupper(buf[j]);
                        Writen(sockfd, buf, n);
                }
                if (--nready <= 0)
                    break;                 /* no more readable descriptors */
            }
        }
    }
View Code

每次select和poll的时候都会把标识符数组从用户态复制到内核态,输出结果再从内核态转到用户态,两次复制效率太低,所以引入了epoll,只需要复制一次,然后从内核态调用内核函数,提高了效率

 

epoll (参考:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html)写的很详细

allset