概述
在C++网络编程中,套接字是实现网络通信的基础。通过套接字,我们除了可以发送和接收数据,还能够配置不同的选项来控制套接字的行为。这些选项可以通过setsockopt函数设置,并通过getsockopt函数获取当前的值。每个选项都有一个级别,表明它影响的是哪一层协议。还有一个名称和值,用于指定具体的选项名称和选项取值。
接下来,我们介绍下getsockopt函数和setsockopt函数的原型。
接口介绍
1、getsockopt函数:用于获取套接字的选项。
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
sockfd:一个有效的套接字描述符。
level:指定要操作的协议层。SOL_SOCKET表示通用套接字选项,IPPROTO_TCP或IPPROTO_UDP分别对应TCP或UDP的特定选项,IPPROTO_IP对应IP层选项。
optname:要设置的具体选项的名称,可参考后面介绍的内容。
optval:指向一个缓冲区,用于存储获取到的选项值。
optlen:输入输出参数。调用前,指向一个变量,该变量包含了optval缓冲区的大小。函数返回后,这个变量将被更新为实际写入optval的数据大小。
返回值:成功时返回0,如果发生错误,则返回-1。发生错误时,errno会被设置为相应的错误代码,比如:EINVAL表示无效参数,ENOPROTOOPT表示协议不支持该选项。
2、setsockopt函数:用于设置套接字的选项。
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
sockfd:同上。
level:同上。
optname:同上。
optval:指向包含新选项值的缓冲区的指针。
optlen:缓冲区optval中数据的长度,以字节为单位。
返回值:同上。
通用套接字选项
通用套接字选项是指那些可以应用于所有类型的套接字的选项,这些选项通常用于控制套接字的一般行为,而不是特定于某种协议的行为。比较常见的通用套接字选项参考如下。
SO_BROADCAST:允许发送广播数据报,通常仅用于UDP套接字,因为TCP协议一般不支持广播。
SO_REUSEADDR:允许绑定到一个地址,即使该地址已经在使用中。这个选项主要用于服务器程序,以允许快速重启,或在同一台机器上运行多个实例。
(1)快速重启。当服务器进程终止后,操作系统通常会保留一段时间(即TIME_WAIT状态)来确保所有已发送的数据包都被接收方收到。这段时间内,相同的IP地址和端口组合不能被重新使用。SO_REUSEADDR选项允许新的服务器实例立即绑定到同一个地址和端口,而不需要等待TIME_WAIT状态结束。
(2)多个实例。允许多个套接字绑定到同一个IP地址和端口。这对于需要在一台机器上运行多个相同服务实例的情况非常有用,比如:负载均衡。此时,客户端的数据会被操作系统内核分配给其中一个进程。这种分配通常是基于负载均衡算法来实现的,但具体的行为依赖于操作系统的实现。
SO_KEEPALIVE:用于启用TCP连接的保持活动机制。这个选项启用后,可检测对端是否仍然在线,特别是在长时间没有数据传输的情况下。本地端会定期发送探测包,如果对端在一定时间内没有响应这些探测包,那么可以认为对端已经崩溃,或者网络连接中断。
SO_LINGER:控制关闭连接时的行为,对应结构体为struct linger,主要分为以下两种情况。
(1)立即关闭。如果l_onoff设置为零,那么close或shutdown函数会立即返回,任何未发送的数据都会被丢弃。
(2)等待未发送数据。如果l_onoff设置为非零值,close或shutdown函数调用会阻塞,直到所有排队的数据都被发送完毕,或者达到超时时间(由l_linger指定)。
SO_ERROR:用于获取与套接字相关的最后一个错误代码。这个选项在处理非阻塞I/O和异步操作时非常有用,因为它允许我们在不阻塞的情况下,检查套接字的状态和错误信息。
SO_RCVBUF:用于设置与套接字关联的接收缓冲区大小。在某些情况下,增加接收缓冲区可以帮助提高吞吐量,特别是在带宽较高的网络环境下。合理配置接收缓冲区大小,能够帮助我们更有效地管理内存资源,避免因过大的缓冲区而导致系统资源紧张。
SO_SNDBUF:用于设置与套接字关联的发送缓冲区大小。较大的缓冲区可以容纳更多的未发送数据,在高延迟或带宽受限的网络环境中尤其有用。对于不同特性的网络环境,可能需要配置不同的发送缓冲区大小以达到最佳性能。
SO_RCVTIMEO:用于设置与套接字关联的接收操作(比如:recv、read或recvfrom函数)的超时时间。这个选项允许用户定义一个时间段,在这段时间内如果没有任何数据被接收,则相关的调用将返回错误。同时,还会设置errno为EAGAIN或EWOULDBLOCK。这对于需要控制等待时间的应用程序非常有用,特别是在网络连接不稳定的情况下。
SO_SNDTIMEO:用于设置与套接字关联的发送操作(比如:send、write或sendto函数)的超时时间。这个选项允许用户定义一个时间段,在这段时间内如果数据无法被成功发送出去,则相关的调用将返回错误。同时,还会设置errno为EAGAIN或EWOULDBLOCK。这在需要控制发送等待时间的应用程序中非常有用,特别是在网络不稳定或对端接收能力有限的情况下。
TCP特定选项
TCP_NODELAY:用于控制TCP连接中的Nagle算法。Nagle算法是一种流量控制机制,目的是为了减少网络拥塞和提高效率。它通过延迟发送小的数据包,来减少网络上的数据段数量。当启用TCP_NODELAY时,Nagle算法被禁用,从而允许更小的数据包立即发送,而不是等待以组合成更大的数据包。
TCP_MAXSEG:用于设置或查询TCP连接的最大分段大小(MSS)。MSS是TCP协议中用来定义在一个单独的TCP数据包中可以包含的最大数据量的参数,不包括TCP头部和IP头部,仅指TCP数据部分。合理设置MSS可以帮助减少IP分片的情况,因为过大的数据包可能会在网络路径中的某些点被分片,这会增加额外的处理开销,并可能导致更多的丢包重传。
TCP_KEEPIDLE:用于设置TCP连接在发送保活探测之前需要保持空闲的时间。这个选项可以帮助检测半开的连接,即一方已经崩溃或网络中断,而另一方没有察觉的情况。通过使用保活探测,可以定期检查连接是否仍然有效。
TCP_KEEPCNT:用于设置在TCP连接中发送保活探测时,如果未收到对端响应,允许的最大重试次数。
TCP_KEEPINTVL:用于设置在TCP连接中发送保活探测的时间间隔。
UDP特定选项
UDP_ENCAP:是一个特定于Linux内核的套接字选项,允许用户在UDP数据包中封装其他协议的数据。该选项主要用于网络研究、隧道技术或需要将非标准协议通过标准UDP端口传输的情况,使得开发者可以在UDP头部之后直接插入自定义的协议头部和数据,而不需要修改内核代码。
UDP_CORK:该选项仅Linux内核中可用,它允许延迟发送数据包,直到禁用该选项为止。这可以帮助减少小数据包的数量,从而提高网络效率,对于需要批量发送数据的应用程序来说非常有用。
int main()
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
// 启用UDP_CORK
int cork = 1;
setsockopt(sock, IPPROTO_UDP, UDP_CORK, &cork, sizeof(cork));
// 发送多个小数据包
const char *messages[] = {"Hello, ", "Hope Wisdom"};
for (const char *msg : messages)
{
sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)&server_addr,
sizeof(server_addr));
}
// 禁用UDP_CORK,触发发送
cork = 0;
setsockopt(sock, IPPROTO_UDP, UDP_CORK, &cork, sizeof(cork));
close(sock);
return 0;
}
IP特定选项
IP_TTL:用于限制数据包在网络中的生存时间。这个字段的值在每次数据包经过一个路由器时递减1,当TTL字段的值达到0时,数据包将被丢弃。同时,会向源主机发送一个ICMP “Time Exceeded”的消息。
IP_MULTICAST_IF:用于指定发送多播数据包时,使用的网络接口。在多播通信中,一个主机可能有多个网络接口,每个接口都连接到不同的网络。通过设置该选项,可以明确指定使用哪个网络接口来发送多播数据包,这对于确保多播流量只在特定的网络上传输非常有用。
IP_MULTICAST_TTL:用于设置多播数据包的生存时间,与IP_TTL类似,故这里不再赘述。
IP_MULTICAST_LOOP:用于控制是否将多播数据包回环到发送该数据包的本地主机。当这个选项被启用时,发送的多播数据包也会被传递给同一主机上的其他应用程序,这些应用程序正在监听相同的多播地址。这在某些情况下非常有用,比如:当你希望在同一台机器上运行的多个进程之间共享多播消息时。