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. 当我们说线程安全时,到底在说什么——Java进阶系列(二)

    原创文章,同步发自作者个人博客,转载请以超链接形式在文章开头处注明出处http://www.jasongj.com/java/thread_safe/ 多线程编程中的三个核心概念 原子性 这一点,跟数 ...

  2. iOS开发~CocoaPods使用详细说明

    一.概要 iOS开发时,项目中会引用许多第三方库,CocoaPods(https://github.com/CocoaPods/CocoaPods)可以用来方便的统一管理这些第三方库. 二.安装 由于 ...

  3. new date() 在Linux下引起的时间差问题

    java工程部署到Linux时,使用new date()获取的时间出现时间差,通过查阅资料,发现有可能是服务器时间设置问题,JVM问题,jdk问题: 1.服务器时间设置问题: 正确的时间显示 有 CS ...

  4. linuxz终端开启echo颜色显示

    echo输出命令echo [选项] [输出内容]-e //支持反斜线控制的字符转换:控制字符:\a //输出警告音:\b //退格键,也就是向左删除键:\n //换行符:\r //回车键:\t //制 ...

  5. install vim

    常用命令: [0]安装vim: oee@copener:~$ sudo apt-get install vim vim-scripts vim-doc 刚安装完$HOME目录下只有两个文件:.vim/ ...

  6. hrbust 1481

    /* 构造特定条件的68序列:给定目标串中6 8 68 86 这四种字串的个数a b p q ,输出所有满足条件的字符串中代表的数字最小的那个 // hrboj 1481 ______________ ...

  7. Python 批量修改文件名

    最近下载了几部美剧(越狱.迷失.权利的游戏......),每集文件名都好长好长..想改短一些,但一个一个改太累了,于是写了个脚本来实现批量修改: 修改前文件名: 修改后文件名: 代码实现: #enco ...

  8. Unity全视角游戏的键盘操作位移——研究笔记

    using UnityEngine; using System.Collections; public class MoveCeShi : MonoBehaviour { ; private Char ...

  9. codeMirror的简单使用,js比较文本差异(标注出增删改)

    最近项目需要使用比较文本的差异的功能,在同事的推荐下,使用js脚本来比较,所以codeMirror变成了选择. 当然codeMirror中有其他功能,比较文本差异的只是其中一个功能,本人不在此做介绍, ...

  10. 基于jQuery的email suggest插件

    最近项目中有表单提交的地方需要用户填写邮箱,PM(产品经理)和运营都强烈要求在用户填写邮箱的时候出现suggest列表,简化用户输入的填写流程.我考虑了下,这个应该也是经常会用到的功能,细心的朋友可能 ...