转自:http://www.cppblog.com/myjfm/archive/2011/10/26/159093.aspx

select()函数和poll()函数均是主要用来处理多路I/O复用的情况。比如一个服务器既想等待输入终端到来,又想等待若干个套接字有客户请求到达,这时候就需要借助select或者poll函数了。

(一)select()函数

原型如下:

1 int select(int fdsp1, fd_set *readfds, fd_set *writefds, fd_set *errorfds, const struct timeval *timeout);

各个参数含义如下:

  • int fdsp1:最大描述符值 + 1
  • fd_set *readfds:对可读感兴趣的描述符集
  • fd_set *writefds:对可写感兴趣的描述符集
  • fd_set *errorfds:对出错感兴趣的描述符集
  • struct timeval *timeout:超时时间(注意:对于linux系统,此参数没有const限制,每次select调用完毕timeout的值都被修改为剩余时间,而unix系统则不会改变timeout值)

select函数会在发生以下情况时返回:

  1. readfds集合中有描述符可读
  2. writefds集合中有描述符可写
  3. errorfds集合中有描述符遇到错误条件
  4. 指定的超时时间timeout到了

当select返回时,描述符集合将被修改以指示哪些个描述符正处于可读、可写或有错误状态。可以用FD_ISSET宏对描述符进行测试以找到状态变化的描述符。如果select因为超时而返回的话,所有的描述符集合都将被清空。
select函数返回状态发生变化的描述符总数。返回0意味着超时。失败则返回-1并设置errno。可能出现的错误有:EBADF(无效描述符)、EINTR(因终端而返回)、EINVAL(nfds或timeout取值错误)。
设置描述符集合通常用如下几个宏定义:

1 FD_ZERO(fd_set *fdset);                /* clear all bits in fdset           */
2 FD_SET(int fd, fd_set *fdset);         /* turn on the bit for fd in fd_set  */
3 FD_CLR(int fd, fd_set *fdset);         /* turn off the bit for fd in fd_set */
4 int FD_ISSET(int fd, fd_set *fdset);   /* is the bit for fd on in fdset?    */

如:

1 fd_set rset;
2 FD_ZERO(&rset);                        /* initialize the set: all bits off  */
3 FD_SET(1, &rset);                      /* turn on bit for fd 1              */
4 FD_SET(4, &rset);                      /* turn on bit for fd 4              */
5 FD_SET(5, &rset);                      /* turn on bit for fd 5              */

当select返回的时候,rset位都将被置0,除了那些有变化的fd位。
当发生如下情况时认为是可读的:

  1. socket的receive buffer中的字节数大于socket的receive buffer的low-water mark属性值。(low-water mark值类似于分水岭,当receive buffer中的字节数小于low-water mark值的时候,认为socket还不可读,只有当receive buffer中的字节数达到一定量的时候才认为socket可读)
  2. 连接半关闭(读关闭,即收到对端发来的FIN包)
  3. 发生变化的描述符是被动套接字,而连接的三路握手完成的数量大于0,即有新的TCP连接建立
  4. 描述符发生错误,如果调用read系统调用读套接字的话会返回-1。

当发生如下情况时认为是可写的:

  1. socket的send buffer中的字节数大于socket的send buffer的low-water mark属性值以及socket已经连接或者不需要连接(如UDP)。
  2. 写半连接关闭,调用write函数将产生SIGPIPE
  3. 描述符发生错误,如果调用write系统调用写套接字的话会返回-1。

注意:
select默认能处理的描述符数量是有上限的,为FD_SETSIZE的大小。
对于timeout参数,如果置为NULL,则表示wait forever;若timeout->tv_sec = timeout->tv_usec = 0,则表示do not wait at all;否则指定等待时间。
如果使用select处理多个套接字,那么需要使用一个数组(也可以是其他结构)来记录各个描述符的状态。而使用poll则不需要,下面看poll函数。

(二)poll()函数

原型如下:

1 int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

各参数含义如下:

  • struct pollfd *fdarray:一个结构体,用来保存各个描述符的相关状态。
  • unsigned long nfds:fdarray数组的大小,即里面包含有效成员的数量。
  • int timeout:设定的超时时间。(以毫秒为单位)

poll函数返回值及含义如下:

  • -1:有错误产生
  • 0:超时时间到,而且没有描述符有状态变化
  • >0:有状态变化的描述符个数

着重讲fdarray数组,因为这是它和select()函数主要的不同的地方:
pollfd的结构如下:

1 struct pollfd {
2    int fd;                  /* descriptor to check */
3    short events;      /* events of interest on fd */
4    short revents;     /* events that occured on fd */
5 };

其实poll()和select()函数要处理的问题是相同的,只不过是不同组织在几乎相同时刻同时推出的,因此才同时保留了下来。select()函数把可读描述符、可写描述符、错误描述符分在了三个集合里,这三个集合都是用bit位来标记一个描述符,一旦有若干个描述符状态发生变化,那么它将被置位,而其他没有发生变化的描述符的bit位将被clear,也就是说select()的readset、writeset、errorset是一个value-result类型,通过它们传值,而也通过它们返回结果。这样的一个坏处是每次重新select 的时候对集合必须重新赋值。而poll()函数则与select()采用的方式不同,它通过一个结构数组保存各个描述符的状态,每个结构体第一项fd代表描述符,第二项代表要监听的事件,也就是感兴趣的事件,而第三项代表poll()返回时描述符的返回状态。合法状态如下:

  • POLLIN:                有普通数据或者优先数据可读
  • POLLRDNORM:    有普通数据可读
  • POLLRDBAND:    有优先数据可读
  • POLLPRI:              有紧急数据可读
  • POLLOUT:            有普通数据可写
  • POLLWRNORM:   有普通数据可写
  • POLLWRBAND:    有紧急数据可写
  • POLLERR:            有错误发生
  • POLLHUP:            有描述符挂起事件发生
  • POLLNVAL:          描述符非法

对于POLLIN | POLLPRI等价与select()的可读事件;POLLOUT | POLLWRBAND等价与select()的可写事件;POLLIN 等价与POLLRDNORM | POLLRDBAND,而POLLOUT等价于POLLWRBAND。如果你对一个描述符的可读事件和可写事件以及错误等事件均感兴趣那么你应该都进行相应的设置。
对于timeout的设置如下:

    • INFTIM:   wait forever
    • 0:            return immediately, do not block
    • >0:         wait specified number of milliseconds

详述socket编程之select()和poll()函数的更多相关文章

  1. socket编程之 select、poll、kqueue、epoll

    原生API select int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct tim ...

  2. PHP Socket 编程之9个主要函数的使用之测试案例

    php的socket编程算是比较难以理解的东西吧,不过,我们只要理解socket几个函数之间的关系,以及它们所扮演的角色,那么理解起来应该不是很难了,在笔者看来,socket编程,其实就是建立一个网络 ...

  3. socket编程之select(),poll(),epoll()

    socket编程,通信 client端  socket() ----->connect() ------->recv() -----> close(); server端 socket ...

  4. Linux网络编程之select、poll、epoll的比较,以及epoll的水平触发(LT)和边缘触发(ET)

    Linux的网络通信先后推出了select.poll.epoll三种模式. select有以下三个问题: (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大. ...

  5. socket编程之select相关

    FD_ZERO,FD_ISSET这些都是套节字结合操作宏 看看MSDN上的select函数, 这是在select   io   模型中的核心,用来管理套节字IO的,避免出现无辜锁定. int   se ...

  6. socket编程之select()

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 参数 ...

  7. iPhone socket 编程之BSD Socket篇

    iPhone socket 编程之BSD Socket篇 收藏在进行iPhone网络通讯程序的开发中,不可避免的要利用Socket套接字.iPhone提供了Socket网络编程的接口CFSocket, ...

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

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

  9. 03 shell编程之case语句与函数

    本文所有内容均来自当年博主当年学习笔记,若有不足欢迎指正 Shell编程之case语句与函数 学习目标: 掌握case语句编程 掌握shell函数的使用 目录结构: Case语句 Case语句的作用 ...

随机推荐

  1. P2484 [SDOI2011]打地鼠

    差分 代码: #include <bits/stdc++.h> using namespace std; #define INF 1999999999 ][],b[][],c[][]; i ...

  2. python全栈开发day47-jqurey

    一.昨日内容回顾 二.今日内容总结 1.jquery的介绍 1).为什么要用jquery? # window.onload 事件有事件覆盖的问题,因此只能写一个事件. # 代码容错性差 # 浏览器兼容 ...

  3. 手动部署 kubernetes 1.9 记录

    前言 目前 kubernetes 正式版本已经到1.10版本.因为前面有大佬(漠然)已经采完坑,所以自己也试着部署 kubernetes 1.9 体验下该版本的新特性.对于前面部署的 kubernet ...

  4. 让我们了解 Ceph 分布式存储

    前言 最近在学习 kubernetes 过程中,想实现 pod 数据的持久化.在调研的过程中,发现 ceph 在最近几年发展火热,也有很多案例落地企业.在选型方面,个人更加倾向于社区火热的项目,Glu ...

  5. 洛谷 P1571 眼红的Medusa【二分查找】 || 【map】

    题目链接:https://www.luogu.org/problemnew/show/P1571 题目描述 虽然Miss Medusa到了北京,领了科技创新奖,但是他还是觉得不满意.原因是,他发现很多 ...

  6. 搭建elasticsearch可视化插件

    一,搭建本地elasticsearch服务 具体搭建流程,可以参考博客:https://www.cnblogs.com/vipchenwei/p/9156668.html elasticsearch搭 ...

  7. 8.10 正睿暑期集训营 Day7

    目录 2018.8.10 正睿暑期集训营 Day7 总结 A 花园(思路) B 归来(Tarjan 拓扑) C 机场(凸函数 点分治) 考试代码 A B C 2018.8.10 正睿暑期集训营 Day ...

  8. C# 的枚Enum

    简短的解释: enum 关键字用来声明枚举,一种包含一组被称为枚举数列表的 enum myType{ a, b, c,} int num = 1;Console.Write((myType)num); ...

  9. ORACLE 内置函数之 GREATEST 和 LEAST(转)

    Oracle比较一列的最大值或者最小值,我们会不假思索地用MAX和MIN函数,但是对于比较一行的最大值或最小值呢?是不是日常用的少,很多人都不知道有ORACLE也有内置函数实现这个功能:COALESC ...

  10. 查询返回JSON数据结果集

    查询返回JSON数据结果集 设计目标: 1)一次性可以返回N个数据表的JSON数据 2)跨数据库引擎 { "tables": [ { "cols": [ { & ...