zoukankan      html  css  js  c++  java
  • UDP:Socket缓冲区大小修改与系统设置(转载)

    本文转载自:https://blog.csdn.net/test1280/article/details/79776938

    UDP: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为:可由程序设置的缓冲区最大值。

  • 相关阅读:
    LayoutParams继承于Android.View.ViewGroup.LayoutParams.
    Android框架之网络开发框架Volley
    【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解
    Android持久化保存cookie
    android 给url添加cookie
    webview同步cookies
    Android 编程下获得应用程序的签名
    Android UI 优化 使用<include/>和 <merge />标签
    ComponentName
    Android网络类型判断(2g、3g、wifi)
  • 原文地址:https://www.cnblogs.com/dongc/p/12325167.html
Copyright © 2011-2022 走看看