• 套接字的多种可选项


    套接字可选项和I/O缓冲大小

    套接字的多种可选项

    套接字可选项分为 IPPROTO_IPIPPROTO_TCPSOL_SOCKET 三层,各层的含义为:
    IPPROTO_IPIP 协议相关事项;
    IPPROTO_TCP:TCP 协议相关事项;
    SOL_SOCKET:套接字相关的通用可选项。
    下表列出了其中部分可选项,这些可选项无需立即掌握,用到什么学什么即可。
    在这里插入图片描述
    在这里插入图片描述

    函数 - getsockopt & setsockopt

    可选项的读取和设置由如下两个函数来是实现:
    getsockopt & setsockopt

    #include 
    int getsockopt(int sock, int level, int optname, void* optval, socklen_t* optlen);
    // 功能:读取套接字可选项
    // 参数:sock:套接字文件描述符;level:可选项所属协议层;optname:要查看的可选项名称;
    // optval:用于保存查看结果的缓冲地址;optlen:调用函数后,optlen 会保存 optval 返回的可选信息项的字节数
    // 返回值:成功时返回 0,失败时返回 -1。
    int setsockopt(int sock, int level, int optname, void* optval, socklen_t optlen);
    // 功能:更改套接字可选项
    // 参数:sock:套接字文件描述符;level:可选项所属协议层;optname:要更改的可选项名称;
    // optval:保存要更改的选项信息的缓冲地址;optlen:指明参数 optval 所指对象的大小
    // 返回值:成功时返回 0,失败时返回 -1。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    示例:

    #include 
    #include 
    #include 
    #include 
    
    void error_handling(char *message);
    
    int main(int argc, char *argv[]) 
    {
    	WSADATA wsaData;
    	int tcp_sock, udp_sock;
    	int sock_type;
    	int optlen;
    	int state;
    	
    	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
    		error_handling("WSAStartup() error!"); 
    	
    	optlen=sizeof(sock_type);
    	tcp_sock=socket(PF_INET, SOCK_STREAM, 0);
    	udp_sock=socket(PF_INET, SOCK_DGRAM, 0);	
    	printf("SOCK_STREAM: %d \n", SOCK_STREAM);
    	printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);
    	
    	state=getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    	if(state)
    		error_handling("getsockopt() error!");
    	printf("Socket type one: %d \n", sock_type);
    	
    	state=getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
    	if(state)
    		error_handling("getsockopt() error!");
    	printf("Socket type two: %d \n", sock_type);
    	
    	WSACleanup();
    	return 0;
    }
    
    void error_handling(char *message)
    {
    	fputs(message, stderr);
    	fputc('\n', stderr);
    	exit(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    选项 - SO_SNDBUF & SO_RCVBUF

    创建套接字同时将生成 I/O 缓冲
    SO_SNDBUF 可选项表示输出缓冲大小相关信息,SO_RCVBUF 可选项表示输入缓冲大小相关信息。
    这两个选项都是可读可写的。默认的输入输出缓冲大小可能在几万字节(几十)左右。
    可以修改缓冲区大小,但是系统并不一定会完全按照我们的要求进行修改,修改结果可能会有所出入。

    示例:

    #include 
    #include 
    #include 
    #include 
    
    void error_handling(char *message);
    
    int main(int argc, char *argv[])
    {
    	WSADATA wsaData;
    	int sock;  
    	int snd_buf, rcv_buf, state;
    	int len;
    	
    	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
    		error_handling("WSAStartup() error!"); 
    	
    	sock=socket(PF_INET, SOCK_STREAM, 0);	
    	len=sizeof(snd_buf);
    	state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
    	if(state)
    		error_handling("getsockopt() error");
    	
    	len=sizeof(rcv_buf);
    	state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
    	if(state)
    		error_handling("getsockopt() error");
    	
    	printf("Input buffer size: %d \n", rcv_buf);
    	printf("Outupt buffer size: %d \n", snd_buf);
    	
    	WSACleanup();
    	return 0;
    }
    
    void error_handling(char *message)
    {
    	fputs(message, stderr);
    	fputc('\n', stderr);
    	exit(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    选项 - SO_REUSEADDR

    time-wait状态

    主动结束连接的一方(先发送 FIN 消息的)会经历 time-wait 状态。

    地址分配错误(bind() error)

    当使用 Ctrl+C 终止服务器程序时,服务器程序成为主动终止连接的一方,会经历 time-wait 状态。这时服务器之前所用的套接字是无法立即使用的(如果立即执行会发生 bind() error),只能等几分钟再执行或修改端口号(即修改套接字)。
    客户端的 time-wait 状态无需关心,因为它的端口号是动态分配的。

    地址再分配

    有时需要立即重启服务器程序,这可以通过更改可选项 SO_REUSEADDR 的状态来实现。
    SO_REUSEADDR 的默认值为 0,将其修改为 1 即可将 time-wait 状态下的套接字端口号重新分配给新的套接字。

    使用方式:

    int option = 1;
    setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, sizeof(option));
    
    • 1
    • 2

    选项 - TCP_NODELAY

    Nagle 算法是应用于 TCP 层的一个简单算法:只有收到前一数据的 ACK 消息时,Nagle 算法才会发送下一数据。
    TCP 默认使用 Nagle 算法,因此会最大限度地进行缓冲,直到收到 ACK 才将数据发送出去。
    在这里插入图片描述
    Nagle 算法的优点:可以避免产生大量网络流量。如果不使用 Nagle 算法,数据到达输出缓冲后立即发送出去,会产生多个体积很小的包(如上图所示),增加网络负载。
    Nagle 算法的缺点:很多时候会降低传输速度。不使用 Nagle 算法时,数据无需等待 ACK 报文就可以发送出去,没有等待时间。在发送大文件数据时尤其明显。因为传输大文件数据无论是否使用 Nagle 算法都不会产生大量的小数据包,而不使用 Nagle 算法则不用等待 ACK 报文,速度更快。
    应根据情况选择是否禁用 Nagle 算法。

    禁用Nagle算法

    可选项 TCP_NODELAY 默认为 0,表示开启 Nagle 算法,将其修改为 1 即可禁用 Nagle 算法。
    禁用 Nagle 算法的方式:

    int opt_val = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, sizeof(opt_val));
    
    • 1
    • 2

    可以通过 TCP_NODELAY 的值查看 Nagle 算法的设置状态。

    int opt_val = 1;
    int opt_len = sizeof(opt_val);
    getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, &opt_len);
    
    • 1
    • 2
    • 3

    如果正在使用 Nagle 算法,opt_val 变量中会保存 0;如果已禁用 Nagle 算法,则保存 1。

  • 相关阅读:
    【汇编语言02】第2章 寄存器——理论知识
    MMPretrain
    网络安全(黑客)自学
    Hive 时间函数加8小时
    排序算法--插入排序
    openGauss 列存表PSort索引
    Kotlin 作用域函数(apply、also、run、let、takeIf)
    SpringCloudAlibaba入门学习笔记20240408~20240424
    git stash详解
    Nginx+keeplived高可用
  • 原文地址:https://blog.csdn.net/cosx_/article/details/134377651