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网络编程(三)中那 ...
随机推荐
- 【Flask】Sqlalchemy 子查询
### subquery:子查询可以让多个查询变成一个查询,只要查找一次数据库,性能相对来讲更加高效一点.不用写多个sql语句就可以实现一些复杂的查询.那么在sqlalchemy中,要实现一个子查询, ...
- display:inline-block 间隙
IE6/7是不支持display:inline-block属性,只是让其表现的跟inline-block一样,尤其对于inline水平的元素,其表现度可以用perfect一词来形容了. 对于IE8+以 ...
- python脚本发送电子邮件
#!/usr/bin/pythonimport smtplibimport stringHOST='smtp.qq.com'#HOST='mail.qq.com'SUBJECT='Test email ...
- python机器学习——分词
使用jieba库进行分词 安装jieba就不说了,自行百度! import jieba 将标题分词,并转为list seg_list = list(jieba.cut(result.get(" ...
- 介绍一下Hibernate的二级缓存
介绍一下Hibernate的二级缓存 按照以下思路来回答:(1)首先说清楚什么是缓存,(2)再说有了hibernate的Session就是一级缓存,即有了一级缓存,为什么还要有二级缓存,(3)最后再说 ...
- 分布式技术 webservice
web service 是一个平台独立的.低耦合的.自包含的.基于编程的web的应用程序,可使用开发的XML(标准通用标记语言下的一个字表)标准来描述.发布.发现.协调和配置这些应用程序,用于开发分布 ...
- VC 模拟CMD 匿名管道
#include "stdafx.h" #include <Windows.h> #include <stdio.h> #include <stdli ...
- SQL server 2008 T-sql 总结
数据库的实现 1.添加数据:insert [into] 表名 (字段1,字段2,···) values (值1,值2,····) 其中,into可选. 2.修改数据:update 表名 set ...
- XPath 文档 解析XMl
http://www.zvon.org/xxl/XPathTutorial/General_chi/examples.html 简介 XPath由W3C的 XPath 1.0 标准描述.本教程通过实例 ...
- 更改自己iCloud的地区
百度一大堆都是过时的,而且没有用,通过google后发现google+里有一个文章https://plus.google.com/+%E5%90%B4%E5%BF%97%E5%8B%8776/post ...