select函数
头文件 <sys/select.h> <sys/time.h>
int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, const struct timeval* timeout);
该函数允许进程指示内核等待多个事件中的任何一个发送,并且只有在一个或多个事件发生或经历一段时间后才唤醒它
timeval的定义如下:
struct timeval
{
long tv_sec; //秒
long tv_usec; //毫秒
}
- 当timeout参数取NULL时,则select()阻塞等待;
- 当timeout参数结构体中两个项都为0时,则select()立即返回,相当于轮询;
- 当timeout参数结构体中不全为0时,等待指定时间后返回。
select()函数会修改timeout中的值,以表示函数返回距离设定的时间之间的差,即剩余时间。因此,每次循环需要对timeout重新设值。
中间三个参数readfds、writefds和errorfds指定要让内核测试读、写和异常条件的描述符,如果不指定,则设为NULL。
如果把readfds、writefds和errorfds三个参数都设置为NULL,那么select这个时候就可以当计时器适用
maxfdp指待测试的描述符的个数,即最大描述符+1
处理fset类型的数据,需要用到以下几个宏:
FD_ZERO(fd_set *fdset) 清空fdset中的所有元素,即fd每位都赋值0
FD_SET(int fd, fd_set *fdset) 把fd元素添加到fdset, 即第fd位赋值1
FD_CLR(int fd, fd_set *fdset):把fd元素从fdset删除, 即第fd位赋值0
FD_ISSET(int fd, fdset *fdset):检测fd是否在集合fdset中,即检测fdset的第fd位是否为1
每次select返回时,都会修改readfds、writefds和errorfds的值,从而指示哪些描述符已就绪,每个描述符需要进行的操作
由于这三个参数都是值-结果类型,因此每次使用select函数之前,都要把readfds、writefds和errorfds的值重新设置
select的返回值表示已经准备就绪的描述符数量,当出错时返回-1
描述符就绪条件
当描述符就绪时,就会加入到就绪队列。这时该描述符才可能从accept或者select、poll等函数返回
带外异常:IO异常
低水位标记:当接受或发送缓存区的数据字节数小于这个值时,阻塞,大于等于这个值时,返回
1. 满足以下其中一个条件,套接字准备好读
(1) 套接字接收缓冲区的数据字节数 大于等于 套接字接收缓冲区的低水位标记大小 ,低水位标记默认为1
(2) 该连接的读半部关闭,即接收了FIN
(3) 该套接字是一个监听套接字,且有新的连接加入
(4) 有套接字的错误待处理,这样的套接字的读操作不阻塞,并返回-1
2. 满足以下其中一个条件,套接字准备好写的条件
(1)套接字发送缓冲区的数据字节数 大于等于 套接字发送缓冲区的低水位标记大小 ,并且该套接字已连接,或者该套接字不需要连接(如UDP)
(2) 该连接的写半部关闭,对这样的套接字的写操作将产生SIGPIPE信号
(3) 使用connect的套接字已经建立连接
(4) 有套接字错误待处理,将不阻塞,并返回-1,同时把errno设为确定的错误条件
注意:当某个套接字上发生错误时,它会将select的 值结果类型数据 中被标记为即可写,又可读
poll函数
头文件 <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
第一个参数指向一个pollfd结构体类型数组fdarray,pollfd结构体结构如下:
struct pollfd
{
int fd;
short events;
short revent;
}
通过fdarray结构体数组,可以登记要监听的描述符
其中,fd是具体要监听的描述符,event指定监听的事件类型,比如 读、写 或者异常 revent返回监听的结果
具体的说,对于event
要监听读事件,则 event = POLLRDNORM
要监听写事件,则 event = POLLWRNORM
读写都监听,则event = POLLRDNORM | POLLWRNORM
监听错误,则 event = POLLERR
对于revent
要确定套接字是否可读,则 判定条件为: revent &POLLRDNORM
要确定套接字是否可读,则 判定条件为: revent &POLLWRNORM
要确定套接字既可读又可写,则判定条件为: revent & (POLLRDNORM | POLLWRNORM)
要确定套接字是否发生错误,则判定条件为:revent & POLLERR
nfds指定fdarray结构体数组的元素数目
timeout指定poll返回前阻塞的最长时间
正常情况下,poll函数返回就绪描述符的个数,即revent元素值非0的描述符个数,发生错误时,返回-1
shutdown函数
头文件<sys/socket.h>
使用close关闭描述符,有两个限制:
- close把描述符引用减1,仅在该计数变为0时才关闭套接字
- close同时终止读和写两个方向的传送
shutdown可以避免这两个局限
int shutdown(int sockfd, int howto)
该函数的行为依赖于howto的值
SHUT_RD 关闭连接的读这一半,丢弃套接字接收缓冲区的数据,接收到的新的数据都被确认,然后丢弃
SHUT_WR 关闭连接的写这一半, 丢弃套接字发送缓冲区的数据,后跟正常的TCP终止序列,无论套接字的引用描述符是否为0
SHUT_RDWR 同时关闭连接的读半部和写半步,这与调用两次shutdown等效