C++网络编程之套接字选项配置

概述

在C++网络编程中,套接字是实现网络通信的基础。通过套接字,我们除了可以发送和接收数据,还能够配置不同的选项来控制套接字的行为。这些选项可以通过setsockopt函数设置,并通过getsockopt函数获取当前的值。每个选项都有一个级别,表明它影响的是哪一层协议。还有一个名称和值,用于指定具体的选项名称和选项取值。

接下来,我们介绍下getsockopt函数和setsockopt函数的原型。

接口介绍

1、getsockopt函数:用于获取套接字的选项。

C++ 复制代码
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函数:用于设置套接字的选项。

C++ 复制代码
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内核中可用,它允许延迟发送数据包,直到禁用该选项为止。这可以帮助减少小数据包的数量,从而提高网络效率,对于需要批量发送数据的应用程序来说非常有用。

C++ 复制代码
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:用于控制是否将多播数据包回环到发送该数据包的本地主机。当这个选项被启用时,发送的多播数据包也会被传递给同一主机上的其他应用程序,这些应用程序正在监听相同的多播地址。这在某些情况下非常有用,比如:当你希望在同一台机器上运行的多个进程之间共享多播消息时。

相关推荐
C++ 老炮儿的技术栈5 分钟前
什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
c语言·开发语言·c++·qt·算法
若风的雨9 分钟前
【deekseek】P2P通信路由过程
服务器·网络协议·p2p
inputA17 分钟前
【LwIP源码学习6】UDP部分源码分析
c语言·stm32·单片机·嵌入式硬件·网络协议·学习·udp
猪八戒1.023 分钟前
C++ 回调函数和Lambda表达式
c++
玉笥寻珍33 分钟前
Web安全渗透测试基础知识之HTTP参数污染篇
网络·网络协议·安全·web安全·http
源远流长jerry1 小时前
匿名函数lambda、STL与正则表达式
c++
tan180°2 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
一只鱼^_2 小时前
牛客练习赛138(首篇万字题解???)
数据结构·c++·算法·贪心算法·动态规划·广度优先·图搜索算法
玉笥寻珍2 小时前
Web安全渗测试基础知识之SSL交互异常利用篇
网络协议·安全·web安全·网络安全·交互·ssl
李匠20243 小时前
C++GO语言微服务之Dockerfile && docker-compose②
c++·容器