一、回顾前面的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. AngularJS 视图和路由

    在AngularJS之后引用angular-route  路由   ngRoute模块加载声明   AngularJS提供的when和otherwise两个方法来定义应用的路由   otherwise ...

  2. Linux的压缩命令(tar,gzip,zip)

    打包和压缩.打包是指将一大堆文件或目录变成一个总的文件:压缩则是将一个大的文件通过一些压缩算法变成一个小文件. 这源于Linux中很多压缩程序只能针对一个文件进行压缩,这样当你想要压缩一大堆文件时,你 ...

  3. php数组函数-array_reduce()

    array_reduce()函数发送数组中的值到用户自定义函数,并返回一个字符串. 注:如果数组是空的或则初始化值未传递,该函数返回NULL array_reduce(array,myfunction ...

  4. redis 数据导入导出,实例内db迁移

    源实例db0迁移至目标实例db1 [root@172.20.0.1 ~]# cat redis_mv.sh #!/bin/bash redis-cli -h -a password -n keys & ...

  5. springmvc时间类型值传输映射

    背景:springmvc4.3.2+spring4.3.2+mybatis3.4.1 当前台传递的参数有时间类型时,封装的vo对象也有对应的时间类型与之对象, 但是如果此时用对象去接收后台会报错,类型 ...

  6. ssh框架整合意义

    一次次学习,一次次不一样的进一步理解. 一.Struts2.String.Hibernate框架的整合的意义: 1.需要将所有的对象进行统一管理(action动作类:sessionFactory) 2 ...

  7. python 操作mongoDB数据库

    网上关于python 操作mongoDB的相关文章相对不是很多,并且质量也不是很高!下面给出一个完整的 增删改查示例程序! #!/usr/bin/python # -*- coding: utf-8 ...

  8. HTTP通信模拟表单提交数据

    前面记录过一篇关于http通信,发送数据的文章:http://www.cnblogs.com/hyyq/p/7089040.html,今天要记录的是如何通过http模拟表单提交数据. 一.通过GET请 ...

  9. 从配置maven环境到maven项目的新建

    话不多说,直接入正题. 一.配置maven 环境 首先安装最新版支持javaee的eclipse.我这里下载的版本是eclipse-jee-mars-2-win32-x86_64的新版(我是2017年 ...

  10. Android开源项目-Easypermissions

    Easypermissions简化了Android M的运行时权限的申请.结果处理.判断等步骤. 1 相关文档 官方文档: https://github.com/googlesamples/easyp ...