千家信息网

IO多路复用之poll

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,poll提供的功能与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,但poll比select的优点是,不限制所能监视的描述符的数目,但随着所监视描述符的数目的增加,性
千家信息网最后更新 2026年10月31日IO多路复用之poll

poll提供的功能与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,但poll比select的优点是,不限制所能监视的描述符的数目,但随着所监视描述符的数目的增加,性能也会下降


函数原型:

#include

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

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

参数:

fds:结构体指针,该结构体结构如下:

struct pollfd{

    int fd; //所感兴趣的文件描述符

   short events; //用于指定等待的事件

   short revents; //用于指定poll返回时,在该文件描述符上实际发生了的事件

   };

每一个pollfd结构体制定了一个被监视文件描述符,可传递多个该结构体,指示poll监视多个文件描述符

nfds:要监视的描述符的个数

timeout:单位(微秒),timeout指定等待的毫秒数,无论I/O是否准备好,poll都会返回,指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件,立即返回


events域中请求的任何事件都可能在revents域中返回。合法的事件如下:

POLLIN         有数据可读

POLLPRI        有紧迫数据可读

  POLLOUT       写数据不会导致阻塞

  POLLRDNORM      有普通数据可读。

  POLLRDBAND      有优先数据可读。

  POLLWRNORM     写普通数据不会导致阻塞。

  POLLWRBAND     写优先数据不会导致阻塞。

  POLLMSGSIGPOLL    消息可用。

  此外,revents域中还可能返回下列事件:
  POLLER   指定的文件描述符发生错误。

  POLLHUP   指定的文件描述符挂起事件。

  POLLNVAL   指定的文件描述符非法。

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

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


示例代码如下:

编写一个echo server程序,功能是客户端向服务器发送信息,服务器接收输出并原样发送回给客户端,客户端接收到输出到终端

server_poll.c

#include #include #include #include #include #include #include #include #include #define _BLOCKLOG_ 6void usage(char *_proc){    printf("%s [ip] [port]\n",_proc);}int create(char *_ip,int _port){    int listen_fd=socket(AF_INET,SOCK_STREAM,0);    if(listen_fd<0){        perror("socket");        exit(1);    }           struct sockaddr_in local;    local.sin_family=AF_INET;    local.sin_port=htons(_port);    local.sin_addr.s_addr=inet_addr(_ip);    struct linger lig;    int iLen;    lig.l_onoff=1;    lig.l_linger=0;    iLen=sizeof(struct linger);    setsockopt(listen_fd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);    if(bind(listen_fd,(struct sockaddr*)&local,sizeof(local))<0){        perror("bind");        exit(2);    }    if(listen(listen_fd,_BLOCKLOG_)<0){        perror("listen");        exit(3);    }    return listen_fd;}int main(int args,char *argv[]){    if(args!=3){        usage(argv[0]);        return 1;    }       char *ip=argv[1];    int port=atoi(argv[2]);    //创建监听描述符并绑定    int listen_fd=create(ip,port);    nfds_t nfds=64;    struct pollfd fds[nfds];    //初始化描述符    int i=0;    for(;i0 && (fds[i].revents&POLLIN)){//normal events happend                                //接收客户端发来的消息                            ssize_t _size=read(fds[i].fd,buf,sizeof(buf)-1);                            if(_size<0){                                perror("read");                            }else if(_size==0){//client closed                                printf("client shutdown\n");                                close(fds[i].fd);                                fds[i].fd=-1;                                continue;                            }else{                                buf[_size]='\0';                                printf("client# %s",buf);                                //向客户端发送消息                                if(write(fds[i].fd,buf,sizeof(buf)-1)<0){                                    perror("write");                                }                            }                        }                    }                    break;                }                break;        }    }    return 0;}

client_poll.c

#include #include #include #include #include #include #include #include #include void usage(char *proc){    printf("%s [ip] [port]\n",proc);}int creat_socket(){    int fd=socket(AF_INET,SOCK_STREAM,0);    if(fd<0){        perror("socket");        exit(1);    }    return fd;}int main(int argc,char* argv[]){    if(argc!=3){        usage(argv[0]);        exit(1);    }    int fd=creat_socket();    int _port=atoi(argv[2]);    struct sockaddr_in addr;    addr.sin_family=AF_INET;    addr.sin_port=htons(_port);    inet_aton(argv[1],&addr.sin_addr);    socklen_t addrlen=sizeof(addr);    if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){        perror("connect");        exit(2);    }    char buf[1024];    while(1){        memset(buf,'\0',sizeof(buf));        printf("Please Enter:");        fgets(buf,sizeof(buf)-1,stdin);        if(send(fd,buf,sizeof(buf)-1,0)<0){            perror("send");            continue;        }        ssize_t _size=recv(fd,buf,sizeof(buf)-1,0);        if(_size>0){            buf[_size]='\0';            printf("echo->%s",buf);        }  }  return 0;}


运行结果:

服务器端运行结果:

客户端1运行结果:

客户端2运行结果:




最后还有一点,poll()会自动把revents域设置为0,不需要我们手动的去设置

测试程序如下:

以下程序片段只是在上面代码的基础上添加了几行代码,为看的更清楚,我用红色注释了这几行代码

运行出来的结果如下:

现在对上面运行结果进行分析,为简单起见,我们只关注四个结构体,所以以四行为单位


可看到第一次(前四行)的revents都为随机值

第二次(次四行)poll()函数把fd=3的描述符中revents设置为0

第四次poll()函数把fd=3的描述符中revents设置为1(因为此时监听到了客户端请求)所以接下来有:

get a connection...1.0.0.0:0 这条消息

第五次fd=3的revents还是为1,因为此时还没有执行poll(),fd=4的revents是随机值

第六次fd=3和fd=4的events都为1,它们都添加了读事件,而fd=3的revents被poll设置为了0,fd=4的revents为1(因为客户端发送了条消息,使读事件发生,因此有下面一条消息:)

client# hello

第七次和第六次的一致,因为还未被poll()设置

第八次和第七次的一致,但此时fd=4的revents=1是因为客户端按下了Ctrl+c,所以接下来会有:

client shutdown

第九次把fd=4从数组中去掉



《完》

事件 文件 客户 客户端 结构 数据 消息 结果 监视 运行 阻塞 代码 等价 函数 多个 服务器 程序 四行 服务 普通 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网金融与金融科技 互联网科技媒体 广州宏创互联网科技 互联网金融科技创新 苏州互联网科技贷款公司 互联网科技上市公司 多迪互联网科技有限公司 互联网巨头投资的硬核科技 互联网科技公司招聘 中国的互联网科技企业的崛起 黑科技闪耀互联网之光 互联网科技公司有风险吗 阿大互联网科技有限公司 互联网公司黑科技创始人 互联网行业科技股 威龙国际互联网科技公司 华中科技大学互联网的牛人 南通创西澳互联网科技有限公司 互联网科技包装股 国产互联网科技公司 互联网 科技公司 科技类的互联网平台 银川新华互联网科技学校地址 岳阳华臻互联网科技有限公司 荆州市昕露互联网络科技有限公司 杭州壁岐互联网科技有限公司 黑科技 体验场景 互联网大会 产业互联网科技服务公司 互联网科技手机排行榜 现代科技的标志之一的互联网
0