【UNP_socket编程】I/O复用函数:select与poll

文章目录

        • 1.I/O复用简介
        • 2.I/O模型
        • 3.select函数的使用
          • 3.1函数原型
          • 3.2 返回值
          • 3.3传入参数
          • 3.4如何设置传入参数fd_set
          • 3.5select使用
          • 3.6错误代码
        • 4.poll函数的使用
          • 4.1函数原型
          • 4.2参数说明

1.I/O复用简介

I/O复用指定是进程需要具备一种告知内核的能力:使得内核发现进程指定的一个或多个I/O条件就绪(即输入或输出已经准备好被读取,或者描述符已经能承载更多的输出),它就通知进程。这个能力是通过select和poll函数来支持的。

I/O复用典型的应用场景:

  • 当客户处理多个描述符(通常为交互式输入与网络套接字)时
  • 当一个客户同时处理多个套接字
  • 如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字
  • 如果一个服务器既要处理TCP,又要处理UDP
  • 如果一个服务器要处理多个服务或者多个协议

2.I/O模型

  • 阻塞式
  • 非阻塞式
  • I/O复用
  • 信号驱动
  • 异步I/O

3.select函数的使用

3.1函数原型
	#include
	int select(int nfds, fd_set *restrict readfds,fd_set *restrict writefds, fd_set *restrict errorfds,
           struct timeval *restrict timeout);
3.2 返回值
  • 负值:select错误
  • 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
3.3传入参数
  • int n:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
  • fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
  • fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
  • fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
  • struct timeval *timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。如果参数timeout设为NULL则表示select()没有timeout。
3.4如何设置传入参数fd_set

	#include
	
	fd_set allFd;//定义fd_set变量
	
	FD_CLR(inr fd,fd_set* set);//用来清除描述词组set中相关fd的位
	
	FD_ISSET(int fd,fd_set *set);//用来测试描述词组set中相关fd的位是否为真
	
	FD_SET(int fd,fd_set*set);//用来设置描述词组set中相关fd的位
	
	FD_ZERO(fd_set *set);//用来清除描述词组set的全部位
3.5select使用
  • 创建fd_set类型的变量。
  • 利用FD_ZERO清除变量set的全部位
  • 利用FD_SET设置想要检测的描述符
  • 阻塞于select
  • select返回后,根据其返回值判断有几个描述符应该处理,然后利用FD_ISSET测试哪些描述符需要处理,并做相应的处理
  • 如果上一步处理完后,不在需要检测此描述符,则利用FD_CLR清除该描述符
  • 返回第三步
3.6错误代码

执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。

EBADF 文件描述词为无效的或该文件已关闭

EINTR 此调用被信号所中断

EINVAL 参数n为负值。

ENOMEM 核心内存不足

4.poll函数的使用

poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

4.1函数原型
	#include 

	int poll(struct pollfd fd[], nfds_t nfds, int timeout);
4.2参数说明
  • 第一个参数:一个结构数组,struct pollfd结构如下:
  struct pollfd{
	  int fd;              //文件描述符
	  short events;    //请求的事件
	  short revents;   //返回的事件
  };

events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。

poll函数的事件标志符值

常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。

  • 第二个参数nfds:要监视的描述符的数目。
  • 最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

你可能感兴趣的:(网络编程)