Linux网络编程——I/O复用之poll函数

一、回顾前面的select

select优点:

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

select缺点:

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

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

二、poll函数概述

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

poll()函数介绍

头文件:

[csharp] view plain copy

  1. #include <poll.h>

函数体:

[csharp] view plain copy

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

功能:

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

参数:

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

[csharp] view plain copy

  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同时收发

代码:

[csharp] view plain copy

  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. }

运行结果:

原文地址:https://www.cnblogs.com/jiangzhaowei/p/8831083.html

时间: 2024-09-30 15:36:47

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

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

想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程--I/O复用之poll函数> 代码: #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #incl

Linux网络编程:端口复用

在<绑定( bind )端口需要注意的问题>提到:一个网络应用程序只能绑定一个端口( 一个套接字只能绑定一个端口 ). 实际上,默认的情况下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个端口( 8000 ), 验证例子如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #i

Linux网络编程-IO复用技术

IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提供了select.poll.epoll三种接口函数来实现IO复用. 1.select函数 #include <sys/select.h> #include <sys/time.h> int select(int nfds, fd_set *readfds, fd_set *writef

嵌入式 Linux网络编程(二)——TCP编程模型

嵌入式 Linux网络编程(二)--TCP编程模型 一.TCP编程模型 TCP编程的一般模型如下图: TCP编程模型分为客户端和服务器端编程,两者编程流程如下: TCP服务器端编程流程: A.创建套接字: B.绑定套接字: C.设置套接字为监听模式,进入被动接受连接状态: D.接受请求,建立连接: E.读写数据: F.终止连接. TCP客户端编程流程: A.创建套接字: B.与远程服务器建立连接: C.读写数据: D.终止连接. 二.TCP迭代服务器编程模型 TCP循环服务器接受一个客户端的连接

Linux网络编程——多路复用之epoll

目录 Linux网络编程--多路复用之epoll 基础API 实例一.epoll实现在线聊天 实例二.epoll实现在客户端断开后服务端能一直运行,客户端可以多次重连 Linux网络编程--多路复用之epoll ? epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,

linux网络编程实现投票功能

投票系统 1.说明: 写了一个投票系统,过程是先配置好服务器,在写一个网上投票功能,要实现网上投票功能. 其实功能实现还是很简单的,麻烦一点的在于过程比较繁杂,要做的东西还是挺多的! 2.过程: 第一步:配置httpd服务器 先配置好httpd服务器.我先下载好了和http服务器的包后,由于fedora的httpd配置相对比较简单,所以我也只是简单的做了一下修改,如下: 把: <Directory"/var/www/cgi-bin"> AllowOverrideNone O

Linux网络编程函数

转自:http://blog.csdn.net/hrbeuwhw/article/details/8050911 1.字节序函数 #include<netinet.h> uint16_t htons(uint16_t host16bitvalue); uint32_t htonl(uint32_t host32bitvalue); 返回:网络字节序值 uint16_t ntohs(uint16_t net16bitvalue); uint32_t ntohl(uint32_t net32bit

嵌入式 Linux网络编程(四)——Select机制

嵌入式 Linux网络编程(四)--Select机制 一.select工作机制 poll和select,都是基于内核函数sys_poll实现的,不同在于在linux中select是从BSD Unix系统继承而来,poll则是从SYSTEM V Unix系统继承而来,因此两种方式相差不大.poll函数没有最大文件描述符数量的限制.poll和 select与一样,大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,开销随着文件描述符数量的增加而线性增大. select需要驱动程序的支持,驱动

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)--epoll机制 一.epoll简介 epoll是在2.6内核中提出的,是select和poll的增强版本.epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中. 1.epoll函数 #include <sys/epoll.h> int epoll_create(int size); 创建一个epoll的句柄,size表示监听的文件描述的数量 int epoll_ctl(int epfd,