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模型学习的更多相关文章

  1. UNIX网络编程-Select模型学习

    1.相关接口介绍 1.1 select ---------------------------------------------------------------------- #include ...

  2. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

  3. UNIX网络编程——网络I/O模型

    在学习UNIX网络编程的时候.一開始分不清 同步 和 异步,所以还是总结一下,理清下他们的差别比較好. IO分类 IO依据对IO的调度方式可分为堵塞IO.非堵塞IO.IO复用.信号驱动IO.异步IO. ...

  4. UNIX网络编程——select函数的并发限制和 poll 函数应用举例

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置,  ...

  5. 《UNIX网络编程 卷1》之"学习环境搭建"(CentOS 7)

    <UNIX网络编程 卷1>的源码可以从www.unpbook.com下载得到.解压之后的目录为unpv13e. 详细步骤 编译 进入unpv13e目录,按如下步骤编译: ./configu ...

  6. UNIX网络编程 第6章 I/O复用:select和poll函数

    UNIX网络编程 第6章 I/O复用:select和poll函数

  7. Unix网络编程中的五种I/O模型_转

    转自:Unix网络编程中的的五种I/O模型 下面主要是把unp第六章介绍的五种I/O模型. 1. 阻塞I/O模型 例如UDP函数recvfrom的内核到应用层.应用层到内核的调用过程是这样的:首先把描 ...

  8. 记录一次配置unix网络编程环境的过程和遇到的问题

    记录一次搭建unix网络编程环境过程中遇到的问题和总结 计算机环境虚拟机 linuxmint-18-xfce-64bit 1.打开unix网络编程.iso 把目录下的文件复制到某一目录,修改权限,可命 ...

  9. 【Linux/unix网络编程】之使用socket进行TCP编程

    实验一 TCP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本TCP通讯,实现服务器与客户端的信息交互. [实验学时] 4学时 [实验内容] 实现一个服务器 ...

随机推荐

  1. ionic cordova 热更新的一些问题

    因为项目需要用到更新这一块的东西,所以就查了下cordova 的热更新,然后遇到了 一些问题,记录下来备忘. 项目用的是ionic 下载cordova的内容就直接跳过了. 首先是下载cordova的插 ...

  2. webStorm 多个项目并存

    点击file ->settings ->Directories 然后右侧有个Add Content Root 添加某个Root即可看见该root下的所有项目

  3. nginx-1.7.9快速启动连接手机测移动端页面

    大家好,本人之前用nginx熟熟的,后来一段时间不用,当今天再次想使用的时候,发现,悲催的是,竟然忘记怎么用了!噢噢,于是乎,就从网上搜索怎样让nginx和移动端页面联系在一起测试. 可惜的是,我搜索 ...

  4. SQL Server COM 组件创建实例失败

    SQL Server COM 组件创建实例失败   SQL2008数据库总会出现从 IClassFactory 为 CLSID 为 {17BCA6E8-A95D-497E-B2F9-AF6AA4759 ...

  5. python unicode 和 str 类型的关系

    python (2.X)在进行 运行时候字符串运算的时候, 分为两种类型 str, unicode 前者是 二进制的形式进行对字符串的保存, 后者是 以unicode的方式进行保存, 一般的工作方式为 ...

  6. 用unity4.3发布WINDOWS STORE APP应用的方法

    http://www.cnblogs.com/suxsho/ 原创,转载请声明 ============================================================ ...

  7. mysql 根据某字段特定值排序

    比如: 表 :user 字段:orders (值为 1,2,3) 要求根据字段  orders 按2 -> 1 -> 3 排序 使用以下语句实现SELECT *FROM userORDER ...

  8. Examples of complexity pattern

    O(1):constant - the operation doesn't depend on the size of its input, e.g. adding a node to the tai ...

  9. Debian普通用户获取root权限|sudo的安装与配置

    Debian系统的普通用户需要安装软件时,往往会收到“Permission denied”的提示,这时候需要root权限.那么如何在不登陆超级管理员账户的前提下拥有root权限呢?对于大多数Linux ...

  10. 用c#中的WebBrowser抢小米F码,抢小米手机以及自动测试实现原理

    首先是用c#中的WebBrowser控件打开登录网页,很简单,拖拽WebBrowser到Form上,然后给它的Url属性赋值.WebBrowser就会自动navigate to 这个网页. WebBr ...