转自: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. 洛谷 P1571 眼红的Medusa【二分查找】 || 【map】

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

  2. Spring框架学习06——AOP底层实现原理

    在Java中有多种动态代理技术,如JDK.CGLIB.Javassist.ASM,其中最常用的动态代理技术是JDK和CGLIB. 1.JDK的动态代理 JDK动态代理是java.lang.reflec ...

  3. Linux遇到的问题

    一.基本命令 yum list installed | grep docker  查看yum安装了哪些软件 netstat -lnp|grep 8000 查看端口 netstat -plutn | g ...

  4. jquery.pagination.js添加跳转页

    原作者github地址:https://github.com/gbirke/jquery_pagination 在这基础上加入了跳转到指定页. 修改后的jquery.pagination.js /** ...

  5. 安卓工作室 android studio 汉化后,报错。 设置界面打不开。Can't find resource for bundle java.util.PropertyResourceBundle, key emmet.bem.class.name.element.separator.label

    安卓工作室 android studio 汉化后,报错. 设置界面打不开. Android studio has been sinified and reported wrong.The setup ...

  6. 【Android】窗口机制分析与UI管理系统

    类图关系 在看Android的窗口机制之前,先看看其主要的类图关系以及层级之间的依赖与调用关系 1.window在当前的android系统的中的呈现形式是PhoneWindow (frameworks ...

  7. loj#2013. 「SCOI2016」幸运数字 点分治/线性基

    题目链接 loj#2013. 「SCOI2016」幸运数字 题解 和树上路径有管...点分治吧 把询问挂到点上 求出重心后,求出重心到每个点路径上的数的线性基 对于重心为lca的合并寻味,否则标记下传 ...

  8. Python3练习题系列(08)——代码阅读方法及字典跳转表理解

    问题:分析下面代码 cities['_find'] = find_city city_found = cities['_find'](cities, state) 分析过程: 一个函数也可以作为一个变 ...

  9. javascript鼠标滚动

    firefox使用DOMMouseScroll,其他浏览器使用mousewheel当滚动时获取wheelDelta值,firefox使用detail:值为下滚3上滚-3,其他浏览器使用wheelDel ...

  10. Java svnkit check update commit

    import java.io.File; import org.apache.log4j.Logger;import org.tmatesoft.svn.core.SVNCommitInfo;impo ...