poll f服务器

来源:互联网 发布:算法工程师笔试考什么 编辑:程序博客网 时间:2024/06/05 18:23
poll优点:
1.poll监听的文件描述符没有最大数量的限制

2.poll对于select来说包含了一个pollfd结构,pollfd结构包含了要监视的event和发生的revent,而不像select那样使用输入输出的传递方式。所以不需要每次监听都初始化

poll缺点:
1.数量过大以后其效率也会线性下降。
2.poll和select一样也是返回就绪事件的个数,需要遍历文件描述符来判断是那个事件已经就绪,当数量很大时,开销也就很大。
3.select和poll都只能工作在低效的LT(水平触发)模式
4.每次调用poll,都需要把pollfd数组从用户态拷贝到内核态,这个开销在fd很多时会很大
5.内核采用轮询(遍历pollfd数组)的方式来检测就绪事件,这个开销在fd很多时也很大


 int poll(struct pollfd *fds, nfds_t nfds, int timeout);

1

1

fds是一个pollfd的结构体数组。


struct pollfd {

               int   fd;         /* file descriptor */

               short events;     /* requested events */

               short revents;    /* returned events */

           };

1

2

3

4

5

6

1

2

3

4

5

6

这就是这个结构体数组每个元素。fd用来记录对应的文件描述符,events用来表示poll所监听的事件,这个由用户来设置。revents用来表示返回的事件。revents是通过内核来进行操作修改。


这里提供了一些合法事件。


事件说明

POLLIN普通或优先级带数据可读

POLLRDNORM普通数据可读

POLLRDBAND优先级带数据可读

POLLPRI高优先级数据可读

POLLOUT普通数据可写

POLLWRNORM普通数据可写

POLLWRBAND优先级带数据可写

POLLERR发生错误

POLLHUP发生挂起

POLLNVAL描述字不是一个打开的文件

后面的三个参数在events无意义,只能作为返回结果存储在revents。


另外,这里需要说的,这些参数如何设置给events,这些宏相当于每一个占用一个比特位,我们可以去想一下位图,所以,如果我们要进行设置两个事件,就使用|操作,另外,当我们去查看事件是否发生的时候,这个时候我们可以使用revents&事件,如果事件发生了,那么结果大于1。这就是一个简单的位运算的,相信你仔细想想就能够理解。


第二个参数nfds,用来监视的文件描述符的数目。


第三个参数是timeout,用来设置超时时间。


参数说明

-1poll将永远阻塞,等待知道某个时间发生

0立即返回

大于0的值设置正常时间值

返回值 

poll返回revents不为0的文件描述符的个数。 

失败返回-1


总结


poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。


poll使用了events和revents分流的特点,这样可以使得对关心事件只进行注册一次。


poll基于链表进行存储,没有最大连接数的限制,只取决于内存大小。


poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。


poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。


poll的缺点


1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样是不是有意义。


2、poll依然需要进行轮询,所消耗的时间太多。


3、水平触发,效率低



代码:


[cpp] view plain copy

#include<stdio.h>  

#include <sys/types.h>          /* See NOTES */  

#include <sys/socket.h>  

#include<stdlib.h>  

#include<netinet/in.h>  

#include<poll.h>  

#include<string.h>  

  

  

static void usage(const char *str)  

{  

    printf("Usage:%s [serv_ip] [serv_port]\n",str);  

}  

  

static int startup(const char *ip,int port)  

{  

    int sock = socket(AF_INET,SOCK_STREAM,0);  

    if(sock < 0)  

    {  

        perror("socket");  

        exit(1);  

    }  

  

    int opt = 1;  

    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));  

  

    struct sockaddr_in serv_addr;  

    serv_addr.sin_family = AF_INET;  

    serv_addr.sin_addr.s_addr = inet_addr(ip);  

    serv_addr.sin_port = htons(port);  

  

    int ret  = bind(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));  

    if(ret < 0)  

    {  

        perror("bind");  

        exit(2);  

    }  

  

    ret = listen(sock,128);  

    if(ret < 0)  

    {  

        perror("listen");  

        exit(3);  

    }  

    return sock;  

}  

  

//       poll函数第一个参数类型。  

//struct pollfd {  

//  int   fd;         /* file descriptor */  

//  short events;     /* requested events */  

//  short revents;    /* returned events */  

//};  

//  

  

int main(int argc ,char *argv[])  

{  

    if(argc != 3)  

    {  

        usage(argv[0]);  

        return 1;  

    }  

  

    int sock = startup(argv[1],atoi(argv[2]) );  

    struct pollfd peerfd[1024]; //当有就绪事件发生的话会写到这个数组里面。  

  

    peerfd[0].fd = sock;  //首先监听连接套接字。监听它的读事件。  

    peerfd[0].events = POLLIN;  

  

    int timeout = -1;  //表示阻塞式等待。  

    int i = 1;  

  

    for(; i < 1024; ++i)  

    {  

        peerfd[i].fd = -1;    //表示这个位置么有被占用。  

    }  

  

    while(1)  

    {               //int poll(struct pollfd *fds, nfds_t nfds, int timeout);  

        int ret = 0;  

        switch(ret = poll(peerfd,1024,timeout))  

        {  

            case 0:  

                printf("timeout...\n");  

                break;  

            case -1:  

                perror("poll");  

                break;  

            default:  

                {  

                    int i = 0;  

                    for(i = 0; i < 1024; ++i)//遍历数组查看有哪些就绪事件发生了。  

                    {  

                        if(i == 0 && (peerfd[i].revents & POLLIN)  )//有新的连接请求。  

                        {  

                            struct sockaddr_in client;  

                            socklen_t len = sizeof(client);  

                            int new_sock = accept(sock,(struct sockaddr*)&client,&len);  

                            if(new_sock < 0)  

                            {  

                                perror("accept");  

                                continue;  

                            }  

                            printf("get a new client\n");  

                            int j = 1;  

                            for(; j < 1024; ++j)  

                            {  

                                if(peerfd[j].fd < 0)  //找最小的未被使用的位置。  

                                {  

                                    peerfd[j].fd = new_sock;  

                                    peerfd[j].events = POLLIN;  

                                    break;  

                                }  

                            }  

  

                            if(j == 1024 )  

                            {  

                                printf("too many client...\n");  

                                close(new_sock);  

                            }  

                        } //if  

                        else if(i != 0)  

                        {  

                            if(peerfd[i].revents & POLLIN) //客户端有读事件发生。  

                            {  

                                char buf[1024];  

                                ssize_t s =  read(peerfd[i].fd,buf,sizeof(buf) - 1);  

                                if(s > 0)  

                                {  

                                    buf[s] = 0;  

                                    printf("clinet say:%s\n",buf);  

                                    peerfd[i].events = POLLOUT; //读完后监听写事件。  

                                }  

                                else if(s <= 0)  

                                {  

                                    close(peerfd[i].fd);  

                                    peerfd[i].fd = -1;  

                                }  

                            }  

                            else if(peerfd[i].revents & POLLOUT)//写事件就绪。  

                            {  

                                /* 客户端写事件发生 */  

                                /* 使用浏览器测试,写回到客户端,浏览器会解析字符串,显示 Hellp Epoll! */  

                                const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><br/><h1>Hello poll!</h1></html>";  

                                write(peerfd[i].fd, msg, strlen(msg));  

                                  

                                //写完后关闭套接字。  

                                    close(peerfd[i].fd);  

                                    peerfd[i].fd = -1;  

                            }  

  

                        } //else if  

  

                    } //for  

  

                } //default  

  

        } //switch()  

    } //while(1)  

  

    return 0;  

}