select的函数原型
image.png
select()参数详解
nfds:select监视的文件句柄数,一般要设为最大文件描述符值+1
readfds:这个文件描述符集合监视文件集中的任何文件是否有数据可读,当select函数返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符。

writefds:这个文件描述符集合监视文件集中的任何文件是否有数据可写,当select函数返回的时候,writefds将清除其中不可写的文件描述符,只留下可写的文件描述符。

exceptfds:这个文件集将监视文件集中的任何文件是否发生错误,其实,它可用于其他的用途,例如,监视带外数据OOB,带外数据使用MSG_OOB标志发送到套接字上。当select函数返回的时候,exceptfds将清除其中的其他文件描述符,只留下标记有OOB数据的文件描述符。

timeout:本次select()的超时结束时间。这个参数至关重要,它可以使select处于三种状态:

若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;

若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述


image.png

select使用流程

select的一个好处就是可以同时监听listening端口以及那些已经连接完成并且有进行io操作的socket

select的使用方式如下

定义fd_set

select允许我们监听来自标准输入,标准输出,标准错误输出的IO信号,监听标准输入IO信号集

注册将要被监听的fd

通过FD_SET和 FD_CLR可以注册和清除某个fd_set内的fd项,使得在调用select的时候可以监听或者取消监听某个fd

如果IO信号到达,识别并处理

通过FD_ISSET可以判断select所监听的fd_set上的IO是否有状态变化,一旦返回true,则可以对该fd进行操作

select使用事项及技巧

使用select时应该注意,如果select有timeout设置,那么每次select之前都要再重新设置一下timeout的值,因为select成功的话会修改timeout的值。

本例中,如果我们在某次select中捕获到listenfd的IO状态有变,也就是说有新的客户端连接,我们不会马上做客户端的请求处理,而是把连接到的socket fd插入到select的监听集合中,然后继续探测其他监听集有IO状态变化(这里的其他监听集就是每个已经连接的客户端的socket fd的状态),如果有变化则马上处理client的请求。这样做的好处是我们及时处理了已连接的客户端的请求,而不是被新连接的客户端的请求所抢占,反正旧客户端被饿死的情况发生

Linux Socket编程中select的常见用处

accept函数的非阻塞实现

当服务器程序调用了listen函数后,此时就可以接受客服端的connect请求,3次握手后,服务器把建立的连接存入队列中,等待accept函数的调用。每调用一次accept,程序就从队列中取出一个已建立连接的socket。默认方式下,accept处于阻塞状态,将套接字文件属性设置为非阻塞时,accept处于非阻塞状态(其实与该套接字相关的系统调用都是非阻塞的了)。

如果将正在listen的socket设置到readfds中,调用select,如果有客户端connect,select将返回正值,通过宏FD_ISSET可检测到该socket可读,此时再用accept接受新的socket,进行读写。

因此当select返回正值,即检测到有数据请求时,

我们可以使用FD_ISSET(listen, &readset) 检测服务器的监听套接字是否有数据,如果有说明监听到了客户端的连接请求,那么我们调用accept来获取客户端的连接。

由此我们的accept函数的非阻塞实现就是非阻塞的,因为我们只有当监听到服务器监听套接字有连接请求时,才会accept

处理完毕后再循环判断其他客户端连接套接字connfd有没有数据请求。

connect函数的非阻塞实现(TCP)

将打开的socket设为非阻塞的。

发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成。

将打开的socket放进被监视的可写(注意不是可读)文件集合中,用select进行监视,如果可写,用getsockopt 函数来得到error的值,如果为零则connect成功。