有几个函数可以设置影响套接字的选项
- getsockopt 和 setsockopt函数
- fcntl 函数
- 把套接字设为非阻塞
- 信号驱动式I/O中设置套接字属主(进程)
- ioctl函数
getsockopt函数 和 setsockopt函数
#include <sys/socket.h>
int setsockopt(int sockfd, int leval, int opename, const void* optval, socklen_t optlen);
函数返回值:若成功则为0,若出错则为-1。
int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t *opelen);
函数返回值:若成功则为0,若出错则为-1。
参数sockfd
指向一个打开的套接字描述符。参数level
表示级别,解释选项的代码作用于通用套接字,或为作用域某个特定的协议套接字。参数*optval
optval 是一个指向某个变量的指针,stesockopt从*optval中取得选项待设置的新值,getsockopt则把获取的值保存在这个变量中。-
参数*opelen 或 optlen
optlen指定选项*optval的大小。eg: setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); // 开启经受时延算法
*opelen是一个指向某个地址的指针,其所指
套接字选项
- level
#include <netinet/in.h> #include <netinet/tcp.h>
- optname
#include <netinet/in.h> #include <netinet/tcp.h>
- 标志
套接字选项可以粗分为2类:- 启用或禁止某个特性的二元选项,称为标志选项。
- 取得并返回我们可以设置或检查的特定值的选项,称为值选项。
level | optname | get | set | 说明 | 标志 | 数据类型 |
---|---|---|---|---|---|---|
SOL_SOCKET | SO_KEEPALIVE | ● | ● | 周期性的检测连接是否存活 | ● | int |
SOL_SOCKET | SO_RCVBUF | ● | ● | 接收缓冲区的大小 | int | |
SO_SNDBUF | ● | ● | 发送缓冲区的大小 | int | ||
SO_RCVTIMEO | ● | ● | 接收超时 | timeval{} | ||
SO_SNDTIMEO | ● | ● | 发送超时 | timeval{} | ||
SO_REUSEADDR | ● | ● | 允许重用本地地址 | ● | int | |
SO_REUSEPORT | ● | ● | 允许重用本地端口 | ● | int | |
IPPROTO_IP | ||||||
IPPROTO_TCP | TCP_NODELAY | ● | ● | 禁止Nagle算法 | ● | int |
SO_KEEPALIVE 套接字选项
给一个套接字设置保持存活(keep-alive)选项后,如果在2小时内在该套接字的任一方向上都没有数据交换,TCP就自动给对端发送一个保持存活探测分节, 这是一个对端必须响应的分节,会导致下列3种情况:
- 对端以期望的ACK回应,应用进程得不到通知。在又经过2小时后,TCP将发出新的探测分节。
- 对端以RST响应,它告知本端TCP:对端已崩溃已重新启动。
- 对端对保持存活探测分节没有任何响应。TCP将发送另外8个分节,两两像个75秒,试图得到一个响应。
SO_RCVBUF 和 SO_SNDBUF 套接字选项
对于TCP来说,套接字接收缓冲区中可用空间的大小限定了TCP通告对端的窗口大小。TCP套接字接收缓冲区不可能溢出,因为不允许对端发出超过本端所通告的窗口大小的数据,这就是TCP的流量控制。
对于UDP来说,当接受到的数据报装不进套接字缓冲区时,该数据报就被丢弃。
对TCP设置套接字接收缓冲区时函数调用顺序很重要。这是因为TCP的窗口规模选项是在建立连接时用SYN分节与对端互换得到的。
所以对于客户端而言,必须在调用connect
之前设置。
对于服务器而言,必须在调用listen
之前设置。
SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项
这2个选项允许我们给套接字的接收和发送设置一个超时值。默认情况下,超时都是禁止的。
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec // 微妙
}
接收超时影响的5个函数:read,readv,recv,recvfrom和recvmsg。
发送超时影响的5个函数:write,writev,send,sendto 和 sendmsg。
SO_REUSEADDR 和 SO_REUSEPORT 套接字选项
SO_REUSEADDR 和 SO_REUSEPORT 允许启动一个监听服务器绑定一个端口,即使以前建立该端口的连接仍然存在。这个情况通常是:
【1】启动一个监听服务
【2】连接请求到达,派生1个子进程处理
【3】监听服务器终止,但子进程继续外现有连接是上的客户提供服务
【4】重启监听服务器
默认情况下,服务器在4步骤时调用bind时,由于它试图捆绑一个现有连连接(子进程)上的端口,bind调用会失败。但是如果设置了SO_REUSEADDR和SO_REUSEPORT,那么bind将会成功。所有的TCP服务器都应该指定本套接字选项。
- TIME_WAIT状态有两个存在的理由:
- 可靠地实现TCP全双工连接的终止
如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。 - 允许老的重复分节在网络中消逝
如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。
- 可靠地实现TCP全双工连接的终止
SO_RCVLOWAT 和 SO_SNDLOWAT 套接字选项
每个套接字还有一个接收低水位标记和一个发送低水位标记,他们由select函数使用。
接收低水位标记是让select返回"可读"时套接字接收缓冲区中最小的数据量,对于TCP,UDP和SCTP套接字,其默认值为1。
发送低水位标记是让select返回"可写"时套接字发送缓冲区所需要的最小可用空间。对于TCP套接字,默认值通常为2048。