1. 获取、设置套接字选项
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
//成功返回0, 出错返回-1
getsockopt将获取的套接字选项的值存放到optval中,而setsockopt从optval中取得带设置的选项
2. 常用套接字选项
SO_KEEPALIVE
给TCP设置一个keep-alive选项后,如果2小时内该套接字没有数据交换, TCP就自动发送一个保持存活探测分节。有以下三种情况:
1. 对端以期望的ACK响应
2. 对端响应RST,它告知本端TCP: 对端已崩溃且重新启动。该套接字的待处理错误为ECONNRESET, 套接字本身关闭
3. 对端保持存活探测分节无响应。将另外发送8个探测分节,两两相差75秒,视图得到响应。如仍未响应则放弃。
SO_KEEPALIVE选项通常由服务器使用,为了检测那些半开连接并终止他们(由于客户端连接掉线、崩溃导致的)
SO_LINGER
SO_LINGER指定close函数对面向连接的协议(TCP和SCTP,不是UDP)如何操作。默认情况close会立即返回,如果发送缓冲区有残留数据,系统会尝试将这些数据发送给对端。
struct linger{
int l_onoff; //0=off, nonzero=on
int l_linger;
};
1. l_onoff = 0 表示不使用该选项
2. l_onoff != 0; l_linger = 0; 表示当close某个连接时,TCP将终止改连接。就是说TCP将丢弃发送缓冲区中的残留数据,并发送RST给对端,而没有通常的四分组连接终止序列。这样虽然可以避免TIME_WAIT状态,但可能在2MSL内创建该连接的另一个化身,导致刚被终止的连接上的旧的重复分节被不正确的发送到新的化身身上。
3. l_onoff !=0; l_linger != 0; 表示套接字关闭时内核拖延一段时间。如果发送缓冲区仍有残留数据,那么进程将被投入睡眠, 直到a)所有数据都已发送完且被对方确认;b)延滞时间到。
有个基本原则:设置SO_LINGER套接字选项,close的成功返回只是告诉我们先前发送的数据和FIN已有对端确认,而不能告诉我们对端是否已读取数据。
在服务器应用程序读取剩余数据之前,服务器主机可能崩溃,并且客户应用程序永远都不会知道。让客户知道服务器已读取剩余数据的一个办法是调用shutdown,第二个参数为SHUT_WR,后跟一个read调用。
SO_RCVBUF和SO_SNDBUF
TCP,UDP和SCTP都有一个发送缓冲区和一个接收缓冲区。TCP和SCTP提供流量控制,套接字接收缓冲区可用空间大小限定了TCP通告对端窗口的大小,不会有缓冲区溢出的情况,因为不允许对端发送超过本端所通告窗口的大小,如果对端无视窗口大小而发送超过窗口大小的数据,本端TCP将丢弃他们。而UDP是不支持流量控制的,较快的发送端可以很容易的淹没较慢的接收端,导致接收端UDP丢弃数据报。
这两个选项可以允许改变这两个缓冲区的默认大小。TCP套接字缓冲区至少应该是相应MSS值的四倍,依据TCP快速恢复算法工作机制。
在设置接收缓冲区大小的时候,对于客户端来说,必须是在connect之前设置,对于服务器而言,必须在listen之前。
在设置套接字缓冲区大小时另一个考虑的问题设计性能,理解的重点在于全双工管道的概念,容量以及如何关系到连接两端套接字缓冲区大小。
SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR有以下四个不同作用
1. 允许启动一个监听服务器,并绑定其众所周知的端口;
2. 允许在同一个端口启动同一个服务器的多个实例。对于TCP,绝不可能启动捆绑相同IP地址和相同端口号的多个服务器;
3.允许单个进程捆绑同一个端口到多个套接字上,每次捆绑需要指定不同的IP地址;
4. 允许完全重复的捆绑。 本特性仅支持UDP套接字。
以上只是常见的,还有其他选项的介绍。
3. fcntl函数
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
//成功则取决于cmd,出错返回-1
使用fcntl开起非阻塞I/O代码:
int flags;
if((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
err_sys("F_GETFL error");
flags |= O_NONBLOCK;
if(fcntl(sockfd, F_SETFL, flags) < 0)
err_sys("F_SETFL error");
如何清除
flags &= ~O_NONBLOCK; //这里flags由上面fcntl调用设置的
if(fcntl(sockfd, F_SETFL, flags) < 0)
err_sys("F_SETFL error");
套接字相关:非阻塞I/O(F_SETFL, O_NONBLOCK), 信号驱动 I/O(F_SETFL, O_ASYNC), 设置套接字属主(F_SETOWN),获取套接字属主(F_GETOWN)