zoukankan      html  css  js  c++  java
  • Socket缓冲区大小修改与系统设置

    每个Socket在Linux中都映射为一个文件,并与内核中两个缓冲区(读缓冲区、写缓冲区)相关联。

    或者说,每个Socket拥有两个内核缓冲区。

    有时,我们需要修改缓冲区的内核限制的最大值,使其符合我们的实际需求。

    一、系统设置

    复制代码

    [jiang@localhost ~]$ uname -a
    Linux localhost.localdomain 2.6.32-642.el6.x86_64 #1 SMP Tue May 10 17:27:01 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
    [jiang@localhost ~]$ cat /proc/sys/net/core/rmem_max
    124928
    [jiang@localhost ~]$ cat /proc/sys/net/core/wmem_max
    124928
    [jiang@localhost ~]$ cat /proc/sys/net/core/rmem_default
    124928
    [jiang@localhost ~]$ cat /proc/sys/net/core/wmem_default
    124928
    

    复制代码

    rmem_max:一个Socket的读缓冲区可由程序设置的最大值,单位字节;
    wmem_max:一个Socket的写缓冲区可由程序设置的最大值,单位字节;
    rmem_default:一个Socket的被创建出来时,默认的读缓冲区大小,单位字节;
    wmem_default:一个Socket的被创建出来时,默认的写缓冲区大小,单位字节;

    注:/proc是一个很特殊的文件系统,其并非真实存在于物理磁盘,而是当前系统运行状态的一个映射,存在于RAM中。

    二、应用程序级修改缓冲区大小

    我们可以在程序中动态地修改(通过setsockopt系统调用)持有的有效Socket的读写缓冲区大小。

    复制代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int main(int argc, char **argv)
    {
         if (argc != 2)
         {
             printf("Usage: %s $RCFBUFSIZE
    ", argv[0]);
             goto error;
         }
    
        int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
         if (sockfd < 0)
         {
             printf("create socket error=%d(%s)!!!
    ", errno, strerror(errno));
             goto error;
         }
    
        // 查看系统默认的socket接收缓冲区大小
         int defRcvBufSize = -1;
         socklen_t optlen = sizeof(defRcvBufSize);
         if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &defRcvBufSize, &optlen) < 0)
         {
             printf("getsockopt error=%d(%s)!!!
    ", errno, strerror(errno));
             goto error;
         }
         printf("OS default udp socket recv buff size is: %d
    ", defRcvBufSize);
    
        // 按照执行参数设置UDP SOCKET接收缓冲区大小
         int rcvBufSize = atoi(argv[1]);
         if (rcvBufSize <= 0)
         {
             printf("rcvBufSize(%d) <= 0, error!!!
    ", rcvBufSize);
             goto error;
         }
         printf("you want to set udp socket recv buff size to %d
    ", rcvBufSize);
         optlen = sizeof(rcvBufSize);
         if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvBufSize, optlen) < 0)
         {
             printf("setsockopt error=%d(%s)!!!
    ", errno, strerror(errno));
             goto error;
         }
         printf("set udp socket(%d) recv buff size to %d OK!!!
    ", sockfd, rcvBufSize);
    
        // 查看当前UDP SOCKET接收缓冲区大小
         int curRcvBufSize = -1;
         optlen = sizeof(curRcvBufSize);
         if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &curRcvBufSize, &optlen) < 0)
         {
             printf("getsockopt error=%d(%s)!!!
    ", errno, strerror(errno));
             goto error;
         }
         printf("OS current udp socket(%d) recv buff size is: %d
    ", curRcvBufSize);
    
        close(sockfd);
    
        exit(0);
    
    error:
         if (sockfd >= 0)
             close(sockfd);
         exit(1);
    }

    复制代码

    编译 && 运行:

    [jiang@localhost ~]$ gcc -o main main.c
    [jiang@localhost ~]$ ./main 1024
    OS default udp socket recv buff size is: 124928
    you want to set udp socket recv buff size to 1024
    set udp socket(3) recv buff size to 1024 OK!!!
    OS current udp socket(2280) recv buff size is: 2280

    我们通过setsockopt系统调用成功地修改了sock的接收缓冲区大小。

    但是,代码级的修改缓冲区大小,不是万能的,其受限于系统配置。

    [jiang@localhost ~]$ ./main 1048576
    OS default udp socket recv buff size is: 124928
    you want to set udp socket recv buff size to 1048576
    set udp socket(3) recv buff size to 1048576 OK!!!
    OS current udp socket(249856) recv buff size is: 249856

    可见,我们希望设置接收缓冲区大小为1024*1024B(1MB),但实际并未达到我们的效果,虽然setsockopt成功了!

    我们可以通过修改系统运行时的配置(/proc),来动态地“释放权限”,让应用程序可以设置更大的内核读写缓冲区。

    三、系统配置级修改缓冲区大小

    复制代码

    [jiang@localhost ~]$ su - root
    Password: 
    [root@localhost ~]# echo 262144 > /proc/sys/net/core/rmem_default
    [root@localhost ~]# echo 1048576 > /proc/sys/net/core/rmem_max
    [root@localhost ~]# logout
    [jiang@localhost ~]$ cat /proc/sys/net/core/rmem_default
    262144
    [jiang@localhost ~]$ cat /proc/sys/net/core/rmem_max
    1048576
    [jiang@localhost ~]$ ./main 1048576
    OS default udp socket recv buff size is: 262144
    you want to set udp socket recv buff size to 1048576
    set udp socket(3) recv buff size to 1048576 OK!!!
    OS current udp socket(2097152) recv buff size is: 2097152

    我们在root下,修改了系统运行时的配置:

    /proc/sys/net/core/rmem_default
    /proc/sys/net/core/rmem_max

    我们设置读缓冲区默认值为256KB,最大值为1MB。

    程序运行时,我们希望设置读缓冲区为1MB。

    通过输出信息,我们可以验证,修改/proc中的配置文件,我们使得一个socket默认的读缓冲区为256KB,读缓冲区最大值为1MB。

    setsockopt系统调用级设置受限于系统运行时配置,可以通过修改系统配置,使得程序设置更大的读写缓冲区。
    需要注意的两点:

    1.)当系统关机重启时,对/proc的修改,是否依然存在?

    不会。这就比较重要,若服务器由于异常宕机,重启后失去了原有的设置,就有可能导致接收缓冲区过小,出现UDP丢包的可能。

    2.)为什么我通过setsockopt设置读缓冲区值为rcvBufSize,但实际getsockopt获取的读缓冲区大小是2*rcvBufSize?

    这个是和源码有关系。

    (引用自:https://blog.csdn.net/liuguanghui1988/article/details/53375269)

    case SO_SNDBUF:
    
    if (val > sysctl_wmem_max)
    
    val = sysctl_wmem_max;
    
    if ((val * 2 ) < SOCK_MIN_SNDBUF)
    
    sk->sk_sndbuf = SOCK_MIN_SNDBUF;
    
    else
    
    sk->sk_sndbuf = val * 2 ;

    系统这么做,猜测可能是由于UDP解包封包需要的额外的空间。

    所以,我称r/wmem_max为:可由程序设置的缓冲区最大值。

  • 相关阅读:
    mysql报错:java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
    MD5登陆密码的生成
    15. 3Sum、16. 3Sum Closest和18. 4Sum
    11. Container With Most Water
    8. String to Integer (atoi)
    6. ZigZag Conversion
    5. Longest Palindromic Substring
    几种非线性激活函数介绍
    AI初探1
    AI初探
  • 原文地址:https://www.cnblogs.com/lidabo/p/13936231.html
Copyright © 2011-2022 走看看