转自: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. python的selenium

    from selenium import webdriverChromeDriver="C:\Program Files (x86)\Google\Chrome\Application\ch ...

  2. 第七章|7.4并发编程| I/O模型

    I/O模型 协程是单线程下的并发,并不是对性能都有所提升,一定是监测单个线程下的多个任务的I/O,遇到I/O不要让它阻塞,给它自动切换到其他任务去,这样就能提高单个线程下的运行效率.--->&g ...

  3. 001 Python中的基本类型初步介绍

    这个但是根据书来整理的,显得有些多,也不够完整. 一:介绍 1.为什么使用内置对象 对象类型是语言的一个部分 内置对象构成了每个python程序的核心部分 二:数字 1.**是乘方 2.math数学模 ...

  4. Mybatis insert时返回自增id

    SelectKey在Mybatis中是为了解决Insert数据时不支持主键自动生成的问题,他可以很随意的设置生成主键的方式. 详细看这里:http://www.cnblogs.com/SimonHu1 ...

  5. Sensor传感器(摇一摇)

    <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content ...

  6. 查找mac下腾讯视频下载地址

    mac 腾讯视频下载的视频是不可见的,也许是因为版权原因吧.使用以下方法可以在文件中找到缓存的视频(不过都是被断开的很多短视频). 在terminal输入: cd Library/Containers ...

  7. 【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume

     Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...

  8. Linux/Window 正斜杠 反斜杠

    文件目录结构: Linux 是用正斜杠 目录名区分大小写 Window 是用反斜杠 目录名不区分大小写

  9. java中thread的start()和run()的区别

    1.start()方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码: 通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪 ...

  10. 11,EasyNetQ-调度事件与定时发布

    许多业务流程都要求将事件安排在未来的某个日期. 例如,在与客户进行初次销售联系之后,我们可能希望在将来某个时间安排后续电话. EasyNetQ可以通过其未来发布功能帮助您实现此功能. 例如,我们在这里 ...