网络编程中,我们经常讨论等待某个描述符准备好I/O(读/写)或者等待其上发生一个待处理的异常条件。尽管可读性和可写性对于普通文件这样的描述符显而易见,然而对于引起诸如select返回套接字“就绪”的条件我们必须讨论的更明确些。
套接字Select函数原型:
#include <sys/select.h> #include <sys/time.h> int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout); //maxfdp1 : 指定待测试的描述符的个数,它的值一般是待测试的最大描述符加1; //中间三个参数是要让内核测试读,写,异常条件的描述符; //返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1;
套接字准备好读的条件:
a ) 该套接字接受缓冲区中的数据字节数大于等于套接字接受缓冲区低水位标记的当前大小。对这样的套接字执行读操作不会阻塞并将返回一个大于0的值(也就是返回准备好读入的数据)。我们可以使用SO_RCVLOWAT套接字选项设置该套接字的低水位标记。对于TCP和UDP套接字而言,其默认值为1。
b) 该套接字的读半部关闭(也就是接受了FIN的TCP连接)。对这样的套接字的读操作将不阻塞并返回0.(也就是返回EOF)
c) 该套接字是一个监听套接字且已完成的连接数不为0。对这样的套接字的accept通常不阻塞。(后边可会发文介绍阻塞accept的一种时序条件)
d) 其上有一个套接字错误待处理。对这样的套接字的读操作将不阻塞并返回-1(也就是返回一个错误),同时把errno设置成确切的错误条件。这样待处理错误(pending error)也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。
套接字准备好写的条件:
a) 该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小,并且或者该套接字已连接,或者该套接字不需要连接(如UDP套接字)。这意味着如果我们把这样的套接字设置成非阻塞,写操作将不阻塞并返回一个正值(例如由传输层接受的字节数)。我们可以使用SO_SNDLOWAT套接字选项来设置该套接字的低水位标记。对于TCP和UDP而言,其默认值通常为2048。
b) 该连接的写半部关闭。对这样的套接字的写操作将产生SIGPIPE信号。
c) 使用非阻塞connect的套接字已建立连接,或者connect已经以失败告终。
d) 其上有一个套接字错误待处理。对这样的套接字的写操作将不阻塞并返回-1(也就是返回一个错误),同时把errno设置成确切的错误条件。这些待处理的错误也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。
注意:当某个套接字上发生错误时,它将select标记为即可读又可写。而且,任何UDP套接字只要其发送低水位标记小于等于发送缓冲区大小就总是可写的,这是因为UDP套接字不需要连接。