IO多路复用之poll

来源:互联网 发布:3d人体模型软件 编辑:程序博客网 时间:2024/05/17 04:28

poll和select区别:
① poll服务器监视的文件描述符无上限;
② poll将输入、输出参数进行分离。

一、poll函数
函数格式如下所示:

#include <poll.h>int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现,即poll将输入、输出参数进行分离。

struct pollfd * fdspollfd 结构体定义如下:struct pollfd {    int fd;               /* 文件描述符 */    short events;         /* 等待的事件 */    short revents;        /* 实际发生了的事件 */} ;

  每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。
  每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。
  revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。
  events域中请求的任何事件都可能在revents域中返回。
  合法的事件如下:

    POLLIN        有数据可读。   POLLRDNORM      有普通数据可读。   POLLRDBAND     有优先数据可读。   POLLPRI       有紧迫数据可读。   POLLOUT       写数据不会导致阻塞。   POLLWRNORM     写普通数据不会导致阻塞。   POLLWRBAND     写优先数据不会导致阻塞。   POLLMSGSIGPOLL   消息可用

  此外,revents域中还可能返回下列事件:

    POLLER    指定的文件描述符发生错误。   POLLHUP   指定的文件描述符挂起事件。   POLLNVAL  指定的文件描述符非法。

  这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。

  使用poll()和select()不一样,你不需要显式地请求异常情况报告。
  POLLIN | POLLPRI等价于select()的读事件.
  POLLOUT |POLLWRBAND等价于select()的写事件。
  POLLIN等价于POLLRDNORM |POLLRDBAND
  而POLLOUT则等价于POLLWRNORM。

  例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。

unsigned int nfds,  nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量
int timeout  timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。   timeout指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生   timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。

poll( )返回值:
   成功时,poll()返回结构体中revents域不为0的文件描述符个数。
  如果在超时前没有任何事件发生,poll()返回0。
  
  失败时,poll()返回-1,并设置errno为下列值之一:

    EBADF         一个或多个结构体中指定的文件描述符无效。   EFAULTfds   指针指向的地址超出进程的地址空间。   EINTR      请求的事件之前产生一个信号,调用可以重新发起。   EINVALnfds  参数超出PLIMIT_NOFILE值。   ENOMEM       可用内存不足,无法完成请求。

二、代码演示(服务器只读演示)

#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include<stdlib.h>#include<netinet/in.h>#include<unistd.h>#include<arpa/inet.h>#include<string.h>#include<poll.h>#define SIZE 100static void usage(const char* proc){    printf("usage:%s [local_ip] [local_port]\n",proc);}int startup(char* ip, int port){    int sock = socket(AF_INET, SOCK_STREAM, 0);    if(sock<0)    {        perror("sock");        exit(1);    }    struct sockaddr_in local;    local.sin_family = AF_INET;    local.sin_port = ntohs(port);    local.sin_addr.s_addr = inet_addr(ip);    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) <0)    {        perror("bind");        exit(2);    }    if(listen(sock, 10) <0)    {        perror("listen");        exit(3);    }    return sock;}int main(int argc, char* argv[]){    if(argc != 3)    {        usage(argv[0]);        exit(4);    }    int listen_sock = startup(argv[1], atoi(argv[2]));    struct pollfd evs[SIZE];    int i = 0;    for(; i<SIZE; i++)    {        evs[i].fd = -1;        evs[i].events = -1;        evs[i].revents = -1;    }    evs[0].fd = listen_sock;    evs[0].events = POLLIN;    int timeout = 3000;    while(1)    {        switch(poll(evs, SIZE, timeout))        {        case 0:           // printf("timeout\n");            break;        case -1:            perror("poll");            break;        default:            {                for(i=0; i<SIZE; i++)                {                   if(i==0 && evs[i].revents == POLLIN)                   {                       struct sockaddr_in client;                       socklen_t len = sizeof(client);                       int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);                       if(new_sock < 0)                       {                           perror("accept");                           continue;                       }                       printf("get a client:%s %d\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port));                       int j = 1;                       for(; j<SIZE; j++)                       {                            if(evs[j].fd == -1)                            {                                evs[j].fd = new_sock;                                evs[j].events = POLLIN;                                break;                            }                       }                       if(j == SIZE)                       {                            close(new_sock);                            continue;                       }                   }                   else if(i != 0 && evs[i].revents == POLLIN)                   {                       char buf[1024];                       ssize_t ss = read(evs[i].fd, buf, sizeof(buf)-1);                       if(ss > 0)                       {                           buf[ss] = 0;                           printf("client# %s\n", buf);                       }                       else if(ss == 0)                       {                           printf("client is quit!\n");                           close(evs[i].fd);                           evs[i].fd = -1;                           break;                       }                       else                       {                           perror("read");                           close(evs[i].fd);                           evs[i].fd = -1;                           continue;                       }                   }                   else// write events                   {                   }                }            }        }    }    return 0;}

poll服务器启动:
这里写图片描述

用telnet链接测试:
这里写图片描述

原创粉丝点击