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()函数介绍
头文件:
- #include <poll.h>
函数体:
- int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
监视并等待多个文件描述符的属性变化参数:
fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件
- struct pollfd{
- int fd; //文件描述符
- short events; //等待的事件
- short revents; //实际发生的事件
- };
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同时收发
代码:
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <poll.h>
- int main(int argc,char *argv[])
- {
- int udpfd = 0;
- int ret = 0;
- struct pollfd fds[2];//监测文件描述结构体数组:2个
- struct sockaddr_in saddr;
- struct sockaddr_in caddr;
- bzero(&saddr,sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(8000);
- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
- bzero(&caddr,sizeof(caddr));
- caddr.sin_family = AF_INET;
- caddr.sin_port = htons(8000);
- //创建套接字
- if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
- {
- perror("socket error");
- exit(-1);
- }
- //套接字端口绑字
- if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0)
- {
- perror("bind error");
- close(udpfd);
- exit(-1);
- }
- printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n");
- fds[0].fd = 0; //标准输入描述符
- fds[1].fd = udpfd; //udp描述符
- fds[0].events = POLLIN; // 普通或优先级带数据可读
- fds[1].events = POLLIN; // 普通或优先级带数据可读
- while(1)
- {
- // 监视并等待多个文件(标准输入,udp套接字)描述符的属性变化(是否可读)
- // 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
- ret = poll(fds, 2, -1);
- write(1,"UdpQQ:",6);
- if(ret == -1){ // 出错
- perror("poll()");
- }
- else if(ret > 0){ // 准备就绪的文件描述符
- char buf[100] = {0};
- if( ( fds[0].revents & POLLIN ) == POLLIN ){ // 标准输入
- fgets(buf, sizeof(buf), stdin);
- buf[strlen(buf) - 1] = '\0';
- if(strncmp(buf, "sayto", 5) == 0)
- {
- char ipbuf[16] = "";
- inet_pton(AF_INET, buf+6, &caddr.sin_addr);//给addr套接字地址再赋值.
- printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));
- continue;
- }
- else if(strcmp(buf, "exit")==0)
- {
- close(udpfd);
- exit(0);
- }
- sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));
- }
- else if( ( fds[1].revents & POLLIN ) == POLLIN ){ //udp套接字
- struct sockaddr_in addr;
- char ipbuf[INET_ADDRSTRLEN] = "";
- socklen_t addrlen = sizeof(addr);
- bzero(&addr,sizeof(addr));
- recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen);
- printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);
- }
- }
- else if(0 == ret){ // 超时
- printf("time out\n");
- }
- }
- return 0;
- }
运行结果:
Linux网络编程——I/O复用之poll函数的更多相关文章
- socket网络编程-----I/O复用之poll函数
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/soc ...
- Linux网络编程——tcp并发服务器(poll实现)
想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程——I/O复用之poll函数> 代码: #include <string.h> #include <st ...
- linux网络编程中的shutdown()与close()函数
1.close()函数 int close(int sockfd); //返回成功为0,出错为-1 close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字不能再由cl ...
- Linux 网络编程中的read和write函数正确的使用方式
字节流套接字上的read和write函数所表现的行为不同于通常的文件IO,字节流套接字上调用read和write输入或输出的可能比请求的数量少,然而这不是出错的状态,例如某个中端使read和write ...
- socket网络编程-----I/O复用之select函数
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/soc ...
- Linux网络编程-IO复用技术
IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...
- Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)
Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...
- Linux网络编程(六)
网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...
- Linux网络编程(四)
在linux网络编程[1-3]中,我们编写的网络程序仅仅是为了了解网络编程的基本步骤,实际应用当中的网络程序并不会用那样的.首先,如果服务器需要处理高并发访问,通常不会使用linux网络编程(三)中那 ...
随机推荐
- 20145239杜文超《网络对抗》- Web安全基础实践
20145239杜文超<网络对抗>- Web安全基础实践 基础问题回答 (1)SQL注入攻击原理,如何防御? SQL注入攻击就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查 ...
- ubuntu ssh免密码登录
目前很多服务(ceph,openstack等)都需要用到SSH使用ssh-key进行登录,而不能使用密码进行登录. 下面是配置步骤: 一.在SSH Client生成ssh key pair root@ ...
- Java远程执行Shell命令
1. Jar包:ganymed-ssh2-build210.jar 2. 步骤: a) 连接: Connection conn = new Connection(ipAddr); conn.conne ...
- tomcat 正常启动但不能访问
Eclipse中的Tomcat可以正常启动,不过发布项目之后,无法访问,包括http://localhost:8080/的小猫页面也无法访问到,报404错误.这是因为Eclipse所指定的Server ...
- Linux 修改DNS解决 Could not retrieve mirrorlist" 报错
CentOS yum有时出现“Could not retrieve mirrorlist ”的解决办法——resolv.conf的配置 或者IP配置文件上写入 缺少DNS引起的问题1. 无法识别域名 ...
- shell 读取文件的每一行内容并输出
(1)#!/bin/bash while read linedo echo $linedone < file (2)#!/bin/bash cat file | while read l ...
- Elasticsearch安装笔记
下载安装包 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.2.zip 开始执行bin/./el ...
- 新东方雅思词汇---7.2、warrant
新东方雅思词汇---7.2.warrant 一.总结 一句话总结: warr+ant 英 ['wɒr(ə)nt] 美 ['wɔrənt] n. 根据:证明:正当理由:委任状 vt. 保证:担保:批 ...
- 经典分水岭算法的 C++ 实现
这个程序是研一下学期的计算机视觉课程大作业,完成于 2013/06/16,是对 Soille 和 Vincent(1991)提出的模拟浸没的分水岭算法的实现,详见下面的报告. 源码托管在 Github ...
- 机器学习(四)—逻辑回归LR
逻辑回归常见问题:https://www.cnblogs.com/ModifyRong/p/7739955.html 推导在笔记上,现在摘取部分要点如下: (0) LR回归是在线性回归模型的基础上,使 ...