首页 > 解决方案 > 如何将 UDP socket recv 缓冲区大小增加到 Linux 程序的最大值?

问题描述

我正在尝试增加 UDP 套接字的接收缓冲区大小,但最终大小似乎无法预测:

    LOG_INFO("UDP echo server default receive buffer size : " << rcv_buf << " bytes");

    // increase default buffer sizes
    rcv_buf *= 3;

    LOG_INFO("trying to increase receive buffer size to : " << rcv_buf << " bytes");

    if (!SockWrap::set_recv_buf_size(m_handle, sizeof(m_sockaddr_in), rcv_buf))
        LOG_ERR("unable to set new receive buffer size");

    // checking the new size after possible modifications if any
    rcv_buf = SockWrap::recv_buf(m_handle, sizeof(m_sockaddr_in));

    if (rcv_buf == -1) {
        LOG_ERR("unable to read UDP echo server receive buffer size after modification");
    } else {
        LOG_INFO("UDP echo server new receive buffer size : " << rcv_buf << " bytes");
    }

包装函数是:

bool SockWrap::set_recv_buf_size(int fd, socklen_t len, int size)
{
    // SO_RCVBUF option is an integer
    int n = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, len);

    if (n == -1) {

        LOG_ERR("setsockopt : " << strerror(errno));
        return false;
    }
    return true;
}

int SockWrap::recv_buf(int fd, socklen_t len)
{
    // SO_RCVBUF option is an integer
    int optval;

    if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &len) == -1) {

        LOG_ERR("getsockopt : " << strerror(errno));
        return -1;

    } else
        return optval;
}

输出 :

UDP echo server default receive buffer size : 212992 bytes
trying to increase receive buffer size to : 638976 bytes
UDP echo server new receive buffer size : 425984 bytes

我已经检查了我的系统的限制/proc/sys/net/ipv4

cat udp_rmem_min
4096
cat udp_mem
186162  248216  372324

并且在/proc/sys/net/core

cat rmem_max
212992
cat rmem_default
212992

所以第一个输出看起来很清楚,默认的 recv 缓冲区值是 212992 字节,由rmem_default.

但是后来尺寸增加了,而且比我想要的大得多,rmem_max但仍然不是我想要的。

这个最终值(425984 字节)来自哪里?

这个值是最大值吗?它是否取决于内核当前使用了多少内存?

编辑 :

根据答案,我测试了其他值,似乎甚至可以设置rmem_default为大于rmem_max

echo 500000 > /proc/sys/net/core/rmem_default
cat /proc/sys/net/core/rmem_default
500000

现在setsockopt调用之前,getsockopt返回(一如既往)rmem_default,而不是rmem_default * 2500000。

但是如果我使用setsockopt将值设置为 500000 则getsocktop返回rmem_max * 2425984。

因此,似乎使用/procinterface 可以比setsockopt.

rmem_maxif rmem_defaultcan be greater的目的是什么?

/* from kernel 5.10.63 net/core/sock.c */
case SO_RCVBUF:
    /* Don't error on this BSD doesn't and if you think
     * about it this is right. Otherwise apps have to
     * play 'guess the biggest size' games. RCVBUF/SNDBUF
     * are treated in BSD as hints
     */
    __sock_set_rcvbuf(sk, min_t(u32, val, sysctl_rmem_max));
    break;

static void __sock_set_rcvbuf(struct sock *sk, int val)
{
       /* Ensure val * 2 fits into an int, to prevent max_t() from treating it
        * as a negative value.
        */
        val = min_t(int, val, INT_MAX / 2);
        sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
        /* We double it on the way in to account for "struct sk_buff" etc.
         * overhead.   Applications assume that the SO_RCVBUF setting they make
         * will allow that much actual data to be received on that socket.
         *
         * Applications are unaware that "struct sk_buff" and other overheads
         * allocate from the receive buffer during socket buffer allocation.
         *
         * And after considering the possible alternatives, returning the value
         * we actually used in getsockopt is the most desirable behavior.
         */
        WRITE_ONCE(sk->sk_rcvbuf, max_t(int, val * 2, SOCK_MIN_RCVBUF));
}

但也许这个编辑应该是另一个(相关的)问题。

谢谢你。

标签: c++linuxsocketsnetworkingudp

解决方案


根据文档

SO_RCVBUF
设置或获取最大套接字接收缓冲区(以字节为单位)。当使用setsockopt(2)设置该值时,内核将该值加倍(以便为簿记开销留出空间) ,并且该加倍值由getsockopt(2)返回。默认值由/proc/sys/net/core/rmem_default文件设置,最大允许值由/proc/sys/net/core/rmem_max文件设置。此选项的最小(加倍)值为 256。

所以,在你的情况下,rmem_max212992,所以你的输入从减少638976212992,然后setsockopt()加倍,因此425984


推荐阅读