socket选项
fcntl系统调用是控制文件描述符属性的通用POSIX方法。下面两个是专门读取和设置socket文件描述符属性的方法:
#include<sys/socket.h>
int getsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
int setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);
注意:restrict关键字。
level指定要操作哪个协议的属性,比如IPv4、IPv6、TCP等,option_name选项的名字。成功返回0,失败返回-1,并设置errno。
对服务器而言,有些socket选项只能在listen之前设置才有效,因为连接socket只能有accept返回,而accept从监听队列中接受的连接至少已经完成TCP三次握手的前两步了,这说明服务器已经往被接受的连接上发送了TCP同步报文段。但有的socket选项应该在TCP同步报文段中设置,比如TCP最大报文段选项。解决方案是对监听socket设置这些socket选项,accept返回的连接socket将自动继承这些选项。这些选项包括:SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OOBINLINE、SO_RCVBUF、SO_RCVLOWAT、SO_SNDBUF、SO_SNDLOWAT、TCP_MAXSEG、TCP_NODELAY。
对客户端而言,这些socket选项要在connect函数之前调用,因为connect成功返回后,三次握手已经完成。
一些重要的socket选项:
SO_REUSEADDR:被处于TIME_WAIT状态连接占用的socket地址可以立即被重用。
SO_RCVBUF和SO_SNDBUF选项表示TCP接收缓冲区和发送缓冲区大小,当用setsockopt设置时,系统会将其值加倍,并且不得小于某值。TCP接收缓冲区最小256字节,发送缓冲区最小2048字节。
SO_RCVLOWAT和SO_SNDLOWAT选项表示TCP接收缓冲区和发送缓冲区的低水位标记,一般用于I/O复用用于判断socket是否可读可写。当接收缓冲区中可读数据总数大于低水位标记时,I/O复用系统调用将通知应用程序可以从对应的socket上读取数据;当发送缓冲区中空闲空间大于其低水位标记时,,I/O复用系统调用将通知应用程序可以往对应的socket上写入数据。默认二者都为1字节。
SO_LINGER选项用于控制close系统调用关闭TCP连接时的行为。默认情况下,close立刻返回,TCP模块负责把该socket的发送缓冲区残留数据发送给对方。设置SO_LINGER时,需要传递一个linger结构体:
#include<sys/socket.h>
struct linger
{
int l_onoff;/*开(非0)关(0)*/
int l_linger;/*滞留时间*/
};
(1)l_onoff等于0,此时SO_LINGER不起作用,close用默认行为;
(2)l_onoff不为0,l_linger等于0,此时,close立即返回,TCP模块丢弃发送缓冲区的残留数据,同时给对方发一个复位报文段。这种情况给服务器提供了异常终止一个连接的方法。
(3)l_onoff不为0,l_linger大于0。此时close的行为取决于两个条件,一是发送缓冲区是否有残留数据,而是该socket是阻塞还是非阻塞的。对于阻塞socket,close将等待l_linger时长,直到TCP模块发送完所有残留数据并得到对方确认。如果这段时间内TCP模块没有发送完残留数据并得到对方确认,close系统调用将返回-1并设置errno为EWOULDBLOCK。如果socket是非阻塞的,close将立即返回,此时需要根据返回值和errno判断残留数据是否发送完毕。