一、回顾前面的select

select优点:

目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点

select缺点:

1.每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大,同时每次调用 select() 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大。

2.单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低

二、poll函数概述

select() 和 poll() 系统调用的本质一样,poll() 的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll()没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

poll()函数介绍

头文件:

  1. #include <poll.h>
 

函数体:

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

功能:

监视并等待多个文件描述符的属性变化

参数:

fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件

  1. struct pollfd{
  2. int fd;         //文件描述符
  3. short events;   //等待的事件
  4. short revents;  //实际发生的事件
  5. };

fd:每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示 poll() 监视多个文件描述符。

events:指定监测fd的事件(输入、输出、错误),每一个事件有多个取值,如下:

revents:revents 域是文件描述符的操作结果事件,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在 revents 域中返回.

注意:每个结构体的 events 域是由用户来设置,告诉内核我们关注的是什么,而 revents 域是返回时内核设置的,以说明对该描述符发生了什么事件

nfds:用来指定第一个参数数组元素个数

timeout: 指定等待的毫秒数,无论 I/O 是否准备好,poll() 都会返回.

返回值:

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

失败时,poll() 返回 -1,并设置 errno 为下列值之一:

EBADF:一个或多个结构体中指定的文件描述符无效。

EFAULT:fds 指针指向的地址超出进程的地址空间。

EINTR:请求的事件之前产生一个信号,调用可以重新发起。

EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。

ENOMEM:可用内存不足,无法完成请求。

三、poll示例举例

用poll实现udp同时收发

代码:
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/select.h>
  6. #include <sys/time.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <poll.h>
  11. int main(int argc,char *argv[])
  12. {
  13. int udpfd = 0;
  14. int ret = 0;
  15. struct pollfd fds[2];//监测文件描述结构体数组:2个
  16. struct sockaddr_in saddr;
  17. struct sockaddr_in caddr;
  18. bzero(&saddr,sizeof(saddr));
  19. saddr.sin_family = AF_INET;
  20. saddr.sin_port   = htons(8000);
  21. saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  22. bzero(&caddr,sizeof(caddr));
  23. caddr.sin_family  = AF_INET;
  24. caddr.sin_port    = htons(8000);
  25. //创建套接字
  26. if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
  27. {
  28. perror("socket error");
  29. exit(-1);
  30. }
  31. //套接字端口绑字
  32. if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0)
  33. {
  34. perror("bind error");
  35. close(udpfd);
  36. exit(-1);
  37. }
  38. printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n");
  39. fds[0].fd = 0;      //标准输入描述符
  40. fds[1].fd = udpfd;  //udp描述符
  41. fds[0].events = POLLIN; // 普通或优先级带数据可读
  42. fds[1].events = POLLIN; // 普通或优先级带数据可读
  43. while(1)
  44. {
  45. // 监视并等待多个文件(标准输入,udp套接字)描述符的属性变化(是否可读)
  46. // 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
  47. ret = poll(fds, 2, -1);
  48. write(1,"UdpQQ:",6);
  49. if(ret == -1){ // 出错
  50. perror("poll()");
  51. }
  52. else if(ret > 0){ // 准备就绪的文件描述符
  53. char buf[100] = {0};
  54. if( ( fds[0].revents & POLLIN ) ==  POLLIN ){ // 标准输入
  55. fgets(buf, sizeof(buf), stdin);
  56. buf[strlen(buf) - 1] = '\0';
  57. if(strncmp(buf, "sayto", 5) == 0)
  58. {
  59. char ipbuf[16] = "";
  60. inet_pton(AF_INET, buf+6, &caddr.sin_addr);//给addr套接字地址再赋值.
  61. printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));
  62. continue;
  63. }
  64. else if(strcmp(buf, "exit")==0)
  65. {
  66. close(udpfd);
  67. exit(0);
  68. }
  69. sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));
  70. }
  71. else if( ( fds[1].revents & POLLIN ) ==  POLLIN ){ //udp套接字
  72. struct sockaddr_in addr;
  73. char ipbuf[INET_ADDRSTRLEN] = "";
  74. socklen_t addrlen = sizeof(addr);
  75. bzero(&addr,sizeof(addr));
  76. recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen);
  77. printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);
  78. }
  79. }
  80. else if(0 == ret){ // 超时
  81. printf("time out\n");
  82. }
  83. }
  84. return 0;
  85. }
运行结果:
 

Linux网络编程——I/O复用之poll函数的更多相关文章

  1. socket网络编程-----I/O复用之poll函数

    #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/soc ...

  2. Linux网络编程——tcp并发服务器(poll实现)

    想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程——I/O复用之poll函数> 代码: #include <string.h> #include <st ...

  3. linux网络编程中的shutdown()与close()函数

    1.close()函数 int close(int sockfd); //返回成功为0,出错为-1 close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字不能再由cl ...

  4. Linux 网络编程中的read和write函数正确的使用方式

    字节流套接字上的read和write函数所表现的行为不同于通常的文件IO,字节流套接字上调用read和write输入或输出的可能比请求的数量少,然而这不是出错的状态,例如某个中端使read和write ...

  5. socket网络编程-----I/O复用之select函数

    #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/soc ...

  6. Linux网络编程-IO复用技术

    IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...

  7. Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)

    Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...

  8. Linux网络编程(六)

    网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...

  9. Linux网络编程(四)

    在linux网络编程[1-3]中,我们编写的网络程序仅仅是为了了解网络编程的基本步骤,实际应用当中的网络程序并不会用那样的.首先,如果服务器需要处理高并发访问,通常不会使用linux网络编程(三)中那 ...

随机推荐

  1. tophat的用法

    概述:tophat是以bowtie2为核心的一款比对软件. tophat工作分两步: 1.将reads用bowtie比对到参考基因组上. 2.将unmapped-reads打断成更小的fragment ...

  2. 关于读取本地text文件,自动被添加空格的问题

    最近做一个小程序,读取本地指定路径下的text文件,逐行获取text文本然后再进行处理,结果遇到了一个奇葩问题,先插个图片给各位看官 坑:本地text文件中数据为1123/10(数据反复检查无空格,换 ...

  3. MySQL/MariaDB数据库备份与恢复之mysqlpump入门操作

    创建测试用表:MariaDB [music]>  create table summary(id int,info char(128));Query OK, 0 rows affected (0 ...

  4. MySql数据库的主从配置

    主服务器 192.168.7.182 Centos6.5 MYSQL5.6.10 从服务器 192.168.112.7 Centos6.5 MYSQL5.6.10 主服务器配置(192.168.7.1 ...

  5. 使用Shell脚本查找程序对应的进程ID,并杀死进程

    #!/bin/sh NAME='shell.php' echo $NAME ID=`ps -ef | grep "$NAME" | grep -v "$0" | ...

  6. 集群环境ssh免密码登录设置

    一.准备工作 1) 用客户端工具(ssh client或者putty)连接到linux服务器.在root用户下输入命令 vi /etc/hosts,用vi编辑hosts文件,如下: #127.0.0. ...

  7. vc 判断当前用户是否在管理员组以及是否SYSTEM权限运行

    BOOL IsUserInAdminGroup() //判断是否在管理员组 { BOOL fInAdminGroup = FALSE; HANDLE hToken = NULL; HANDLE hTo ...

  8. QT 布局时使用 addStretch 可伸缩设置

    今天在使用addStretch,布局的时候,发现addStretch竟然是可以平均分配的,有意思.比如: QVBoxLayout *buttonLayout = new QVBoxLayout; bu ...

  9. Java中interface是否继承Object类

    首先我们从C++说起, c++可以多继承.也就是一个类型 --- class,可以继承自2个以上的父类型.多继承导致一个问题,很多人知道.例如,如果类型B,类型C均继承自类型A.然后类型D继承自类型B ...

  10. Codeforces 859E Desk Disorder:并查集【两个属性二选一】

    题目链接:http://codeforces.com/problemset/problem/859/E 题意: 有n个人,2n个座位. 给出这n个人初始的座位,和他们想坐的座位. 每个人要么坐在原来的 ...