一、回顾前面的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. complexHeatmap包画分类热图

    用途:一般我们画热图是以连续变量作为填充因子,complexHeatmap的oncopoint函数可以以类别变量作为填充因子作热图. 用法:oncoPrint(mat, get_type = func ...

  2. poj 3617输出格式问题

    注意是说的80个字母一行....

  3. mini2440移植uboot 2014.04(六)

    上一篇博文:<mini2440移植uboot 2014.04(五)> 代码已经上传到github上:https://github.com/qiaoyuguo/u-boot-2014.04- ...

  4. numpy模块之创建矩阵、矩阵运算

    本文参考给妹子讲python  https://zhuanlan.zhihu.com/p/34673397 NumPy是Numerical Python的简写,是高性能科学计算和数据分析的基础包,他是 ...

  5. JAVAWeb学习总结(一)

    一.基本概念 1.1.WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 静态web资源( ...

  6. StringTemplateLoader的用法

    作为一个模板框架,freemarker的功能还是很强大的.在模板处理方面,freemarker有多种形式,最常见的方式是将模板文件放在一个统一的文件夹下面,如下形式:Configuration cfg ...

  7. 开机启动顺序rc.local与chkconfig的不同

    /etc/rc.local文件有如下两行/etc/init.d/mysql start/etc/init.d/keepalived start /etc/rc.local是按脚本的顺序一个启动后启动下 ...

  8. delphi中httpencode使用注意事项

    delphi中httpencode使用注意事项 一.uses HTTPApp二.使用前要用UTF8Encode转换成utf-8编码HTTPEncode(UTF8Encode(Text));不然和标准的 ...

  9. Anton and School - 2 (组合数学)

    题意:给你一串只有‘(’与‘)’的字符串,问你多少对括号,括号一定是左边一半的‘(’,右边一半是‘)’ )(()()   答案是:6 题解:枚举每个‘(’,此时设左括号左边有n个‘(’,它右边有m个‘ ...

  10. yum 源的配置与使用

    一.yum 简介 yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器.起初是由yellow dog 这一发行版的 ...