UNIX网络编程-Poll模型学习
1、相关接口介绍
1.1 poll
----------------------------------------------------------------------
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
返回:准备好描述字的个数,0—超时,-1—出错。
----------------------------------------------------------------------
参数说明:
fdarray: 是一个pollfd结构类型的指针,指向一个链表,pollfd结构由三部分组成:文件描述符以及要检查的条件和检查后返回的结果;
nfds: 要检查的文件描述符的个数,也就是以上链表中元素的个数;
timeout: 超时时间,单位为毫秒,若为INFTIM则表示永远等待,若为0表示立即返回。
1.2 pollfd
pollfd为一个结构体:
- struct pollfd
- {
- int fd; /* descriptor to check */
- short events; /* events of interest on fd */
- short revents; /* events that occurred on fd */
- };
一下是events和revents可能的值:
常量 |
能作为events的输入吗? |
能作为revents的结果吗? |
解释 |
POLLIN |
yes |
yes |
普通或优先级波段数据可读 |
POLLRDNORM |
yes |
yes |
普通数据可读 |
POLLRDBAND |
yes |
yes |
优先级波段数据可读 |
POLLPRI |
yes |
高优先级数据可读 |
|
POLLOUT |
yes |
yes |
普通或优先级波段数据可写 |
POLLWRNORM |
yes |
yes |
普通数据可写 |
POLLWRBAND |
yes |
yes |
优先级波段数据可写 |
POLLERR |
yes |
发生错误 |
|
POLLHUP |
yes |
发生挂起 |
|
POLLNVAL |
yes |
描述字不是一个打开的文件 |
上图可分为三部分:四个处理输入的常量;三个处理输出的常量;三个处理错误的常量。
poll识别三个类别的数据:普通(normal)、优先级波段(priority band)、高优先级(high priority)。术语来自流的概念。
poll接口返回说明:
所有正规TCP数据和UDP数据都被认为是普通数据;
TCP的带外数据被认为是优先级带数据;
当TCP连接的读这一半关闭时(如接收了一个FIN),这也认为是普通数据,且后续的读操作将返回0;
TCP连接存在错误既可以认为是普通数据,也可以认为是错误(POLLERR)。无论哪种情况,后续的读操作将返回-1,并将errno置为适当的值,这就处理了诸如接收到RST或超时等条件;
在监听套接口上新连接的可用性既可认为是普通数据,也可以认为是优先级带数据,大多数实现都将其作为普通数据考虑。
如果不关心某个特定的描述字,可将其pollfd结构的fd成员置为一个负值,这样就可以忽略成员events,且返回时将成员revents的值置为0。
poll没有select存在的最大描述字数目问题。但可移植性select要好于poll。
2、poll的工作流程跟select差不多,这里直接跳过,来看相应的demo
- #include <stdio.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <strings.h>
- #include <poll.h>
- #define PORT 8080
- #define LISTENQ 5
- #define MAXLINE 1024
- #define OPEN_MAX 1024
- #define IS_ERROR(condition) \
- if(condition) \
- { \
- printf("Error in func[%s] and line[%d]!\n", \
- __PRETTY_FUNCTION__, __LINE__); \
- return ; \
- }
- #ifndef INFTIM
- #define INFTIM (-1)
- #endif
- int main(int argc, char *argv[])
- {
- struct sockaddr_in addrSer;
- struct sockaddr_in addrCli;
- int listenSock;
- int connSock;
- struct pollfd clientSock[OPEN_MAX];
- int sumSock; //sum of client sockets - 1
- int nCliLen; //len of addrCli
- int nReady; //the num of ready sockets
- char buf[MAXLINE];
- int nRet;
- int i;
- /*create listen socket*/
- listenSock = socket(AF_INET, SOCK_STREAM, );
- IS_ERROR(listenSock == -);
- /*bind listen port*/
- bzero(&addrSer, sizeof(addrSer));
- addrSer.sin_family = AF_INET;
- addrSer.sin_addr.s_addr = htonl(INADDR_ANY);
- addrSer.sin_port = htons(PORT);
- nRet = bind(
- listenSock,
- (struct sockaddr *)&addrSer,
- sizeof(struct sockaddr_in)
- );
- IS_ERROR(nRet == -);
- /*listen port*/
- nRet = listen(listenSock, LISTENQ);
- IS_ERROR(nRet == -);
- /*init*/
- clientSock[].fd = listenSock;
- clientSock[].events = POLLRDNORM;
- for (i=; i<OPEN_MAX; ++i)
- {
- clientSock[i].fd = -;
- }
- sumSock = ;
- /*request*/
- while ()
- {
- nReady = poll(clientSock, sumSock+, INFTIM);
- /*accept*/
- if (clientSock[].revents & POLLRDNORM)
- {
- nCliLen = sizeof(addrCli);
- connSock = accept(clientSock[].fd, (struct sockaddr *)&addrCli, &nCliLen);
- for (i=; i<OPEN_MAX; ++i)
- {
- if (clientSock[i].fd < )
- {
- clientSock[i].fd = connSock;
- break;
- }
- }
- if (i == OPEN_MAX)
- {
- printf("too many clients!\n");
- return ;
- }
- clientSock[i].events = POLLRDNORM;
- sumSock = (sumSock < i) ? i : sumSock;
- if (--nReady <= )
- {
- continue;
- }
- }
- /*send and recv*/
- for (i=; i<=sumSock; ++i)
- {
- if (clientSock[i].fd < )
- {
- continue;
- }
- if (clientSock[i].revents & (POLLRDNORM | POLLERR))
- {
- nRet = recv(clientSock[i].fd, buf, MAXLINE, );
- if (nRet == || nRet == -)
- {
- printf("read sock %d err, nRet = %d!\n", clientSock[i], nRet);
- close(clientSock[i].fd);
- clientSock[i].fd = -;
- }
- else if (- == send(clientSock[i].fd, buf, nRet, ))
- {
- printf("write sock %d err!\n", clientSock[i]);
- close(clientSock[i].fd);
- clientSock[i].fd = -;
- }
- if (--nReady <= )
- {
- break;
- }
- } //if (clientSock[i].revents & (POLLRDNORM | POLLERR))
- } //for (i=1; i<=sumSock; ++i)
- } //while (1)
- return ;
- }
3、poll和select对比
3.1 poll和select的优缺点
和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由一个指针指向这个组。
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程跟select一样,经历了多次无谓的遍历。
poll比select好的一点是,没有最大连接数限制,原因是它是基于链表来存储的,而select则会有最大描述符的限制。
3.2 poll和select相对于的点
每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码。内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。
events和revents两个域可设置的事件掩码在1.2中都有介绍。
POLLIN | POLLPRI等价于select()的读事件,POLLOUT | POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM | POLLRDBAND,而POLLOUT则等价于POLLWRNORM。
例如,要同时监视一个文件描述符是否可读和可写,我们可以设置events为POLLIN | POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
UNIX网络编程-Poll模型学习的更多相关文章
- UNIX网络编程-Select模型学习
1.相关接口介绍 1.1 select ---------------------------------------------------------------------- #include ...
- 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数
本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...
- UNIX网络编程——网络I/O模型
在学习UNIX网络编程的时候.一開始分不清 同步 和 异步,所以还是总结一下,理清下他们的差别比較好. IO分类 IO依据对IO的调度方式可分为堵塞IO.非堵塞IO.IO复用.信号驱动IO.异步IO. ...
- UNIX网络编程——select函数的并发限制和 poll 函数应用举例
一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...
- 《UNIX网络编程 卷1》之"学习环境搭建"(CentOS 7)
<UNIX网络编程 卷1>的源码可以从www.unpbook.com下载得到.解压之后的目录为unpv13e. 详细步骤 编译 进入unpv13e目录,按如下步骤编译: ./configu ...
- UNIX网络编程 第6章 I/O复用:select和poll函数
UNIX网络编程 第6章 I/O复用:select和poll函数
- Unix网络编程中的五种I/O模型_转
转自:Unix网络编程中的的五种I/O模型 下面主要是把unp第六章介绍的五种I/O模型. 1. 阻塞I/O模型 例如UDP函数recvfrom的内核到应用层.应用层到内核的调用过程是这样的:首先把描 ...
- 记录一次配置unix网络编程环境的过程和遇到的问题
记录一次搭建unix网络编程环境过程中遇到的问题和总结 计算机环境虚拟机 linuxmint-18-xfce-64bit 1.打开unix网络编程.iso 把目录下的文件复制到某一目录,修改权限,可命 ...
- 【Linux/unix网络编程】之使用socket进行TCP编程
实验一 TCP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本TCP通讯,实现服务器与客户端的信息交互. [实验学时] 4学时 [实验内容] 实现一个服务器 ...
随机推荐
- iOS RSA加密解密及签名验证
1.首先要下载openssl,这个不用说,直接官网下载或者用brew install openssl下载 2.终端生成私钥密钥 2.1生成私钥 openssl genrsa - 2.2生成密钥 ope ...
- ABAP 弹出对话框
一组有用的用户交互窗口函数 显示多条消息 SAP系统用的是这个函数:C14Z_MESSAGES_SHOW_AS_POPUP POPUP_TO_CONFIRM_LOSS_OF_DATA 显示有YES/N ...
- MMDrawerController在表视图和导航栏中的使用
1.如果不在APPDelegate引入MMDrawerController框架,那么就要注意在需要点击的视图控制器中的对象的获取. //工程中标签视图控制器 MainTabBarViewControl ...
- NSLog 自定义 屏蔽
1.如何自定义NSLog呢? 直接在工程的XXX_Prefix.pch中加入以下语句(就相当于在全局中定义了)#define NSLog NSLog(@"#%s##%d#",str ...
- 开发基于C#.NET的mongodb桌面版的应用程序(1)
1.之前没有使用过C#开发过相应的桌面应用程序,现在既然要从零到有进行开发,自然要掌握好C#桌面开发相关的原理与技术,以及站在多类型用户的角度开发具有实际生产意义的mongodb数据库管理软件. 2. ...
- linux下根据进程名字获取PID,类似pidof(转)
linux有一个命令行工具叫做pidof,可以根据用户输入的进程名字查找到进程号,但有时候我们需要在程序里实现,不想调用system,在查阅了很多版本的pidof源代码后,没有发现一个自己感觉比较好的 ...
- CentOS 7下源码安装MySQL 5.6
本文转载,并非原创. 目录 准备工作 运行环境 确认你的安装版本 下载MySQL 安装MySQL 准备安装环境 编译和安装 配置MySQL 单实例配置 单实例配置方法 添加防火墙 启动MySQL 重启 ...
- 用Linq操作数据小记
小记 public void UpdateWarhouse(Administrator admin) { var warhouseStr = Request["warhouse"] ...
- Mysql 日志 (转)
MySQL日志: 主要包含:错误日志.查询日志.慢查询日志.事务日志.二进制日志: 错误日志 在mysql数据库中,错误日志功能是默认开启的.并且,错误日志无法被禁止.默认情况下,错误日志存储在mys ...
- Mysql 安装-操作-备份
Mysql 5.7 安装windows 1.配置mysql的path->系统属性->环境变量-path添加最后 2.配置目录和主从 [mysqld]port = 3306 basedir= ...