首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Linux上设置UDP发送缓冲区

如何在Linux上设置UDP发送缓冲区
EN

Stack Overflow用户
提问于 2022-10-06 15:33:50
回答 1查看 89关注 0票数 1

在我的C++程序中,我发送了许多小的(< KB) UDP消息。当我测量这类UDP发送的性能时,我看到它们中的大多数都在300毫秒以下完成,但偶尔(每到150次),它需要3秒。请注意,这些测量是在wi网络上进行的,这可能是偶尔延迟的原因。然而,如果我想支持wi场景,我怀疑增加发送缓冲区将是我的第一步。我最初的研究指出,通过net.core.rmem_maxnet.core.rmem_default来增加UDP缓冲区的大小,我做到了:

代码语言:javascript
运行
复制
$sudo sysctl net.core.rmem_max
net.core.rmem_max = 26214400
$sudo sysctl net.core.rmem_default
net.core.rmem_default = 26214400

然而,这似乎对我的程序没有任何影响。接下来,我尝试在代码:setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &size, size_len)中设置缓冲区大小,其中的大小是26214400。这解决了问题-没有更多的延迟包。但是,在通过getsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &size, &size_len)检查缓冲区大小之后,我发现缓冲区被设置为5228800

因此,我的问题是:增加UDP发送缓冲区的适当方式是什么?

我的环境:

代码语言:javascript
运行
复制
cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"

这个问题只出现在我使用wi时,这是我想要支持的用例。换句话说,在对以太网接口进行测试时,我看不到偶尔缓慢的发送。

下面是我与UDP相关的源代码:

代码语言:javascript
运行
复制
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <cerrno>

#include <boost/log/trivial.hpp>

#include "udp_sender.h"

void soc_get_so_snd_buf(int socket_fd)
{
    unsigned int size = 0;
    socklen_t size_len = sizeof(size);

    if(getsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &size, &size_len) == -1 ) {
        std::string err_msg = "Failed to get the socket send buffer for socket: " + std::to_string(socket_fd);
        BOOST_LOG_TRIVIAL(error) << err_msg;
        throw std::runtime_error(err_msg);
    }

    BOOST_LOG_TRIVIAL(debug) << "Send buffer size (SO_SNDBUF) is: " << size;
}

void soc_set_so_snd_buf(int sock_fd, size_t size)
{
    socklen_t size_len = sizeof(size);
    if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &size, size_len) == -1) {
        std::string err_msg = "Failed to set the socket send buffer to " + std::to_string(size);
        BOOST_LOG_TRIVIAL(error) << err_msg;
        throw std::runtime_error(err_msg);
    }
}


UDPSender::UDPSender(int id) : my_id(id) {
    char my_ip[100];
    sprintf(my_ip, "10.1.1.%d", id);

    send_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(send_socket_fd < 0)
        throw std::runtime_error("Failed to create a send socket.");


    int enable = 1;
    if (setsockopt(send_socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
        throw std::runtime_error("Failed to assign REUSADDR option.");
    }

    soc_get_so_snd_buf(send_socket_fd);
    soc_set_so_snd_buf(send_socket_fd, 26214400); // setting this to 26214400 results in 5228800 which seems to solve my issue for now.
    soc_get_so_snd_buf(send_socket_fd);

    struct sockaddr_in srcAddr{};
    memset(&srcAddr, 0, sizeof(srcAddr));
    srcAddr.sin_family = AF_INET;
    srcAddr.sin_port = htons(0); // Any port will do.
    inet_pton(AF_INET, my_ip, &srcAddr.sin_addr);
    if(bind(send_socket_fd, (struct sockaddr*)&srcAddr, sizeof(srcAddr)) < 0) {
        BOOST_LOG_TRIVIAL(error) << "Failed to bind sending socket " << strerror(errno);
        throw std::runtime_error("Failed to bind a send socket.");
    }
}

UDPSender::~UDPSender() {
    close(send_socket_fd);
    send_socket_fd = -1;
}

ssize_t UDPSender::send(
    int dest_id,
    int dest_port,
    char* msg,
    size_t msg_size) const {

  
    char dest_ip[100];
    struct sockaddr_in dest_addr{};
    sprintf(dest_ip, "10.1.1.%d", dest_id);
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(dest_port);
    inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr);
    
    ssize_t bytes_sent = sendto(
            send_socket_fd,
            msg,
            msg_size,
            0,
            (struct sockaddr*)&dest_addr, sizeof(dest_addr));
    if (bytes_sent < 0) {
        std::string error_msg = "Failed to send from: " +
                std::to_string(my_id) +
                " to: " +
                std::to_string(dest_id) + " errno: " + std::to_string(errno) + " ";
        error_msg += strerror(errno);

        if (errno == EPERM) {
            error_msg += "; error was due iptables bock rule - expected for some targets.";
            BOOST_LOG_TRIVIAL(debug) << error_msg;
            return 0;
        } else {
            BOOST_LOG_TRIVIAL(error) << error_msg;
            throw std::runtime_error(error_msg);
        }
    }

     if (bytes_sent != msg_size) {
        BOOST_LOG_TRIVIAL(error) << "Failed to send the expected number of bytes, wanted :"
            << msg_size << " sent " << bytes_sent;
        throw std::runtime_error("Failed to send the expected number of bytes.");
    }

    return bytes_sent;
}

测试:

代码语言:javascript
运行
复制
#include "udp_sender.h"
#include "message_factory.h"
#include "heartbeats.h"

#include <thread>
#include <chrono>

#include "gtest/gtest.h"
#include <boost/log/trivial.hpp>

using namespace std::chrono;


TEST(UDPSenderTest, Broadcast) {
    int sender_id = 0;
    UDPSender sender = UDPSender(sender_id);
    char serialized[65000];
    Heartbeat msg = Heartbeat(sender_id, Peers{Node{2, 3}}, false);
    long serialized_size = MessageFactory::serialize_heartbeat(msg, serialized);

    auto start_cycle = high_resolution_clock::now();
    for(int i=0; i<256; i++) {
        auto start = high_resolution_clock::now();
        sender.send(i, 7777,  serialized, serialized_size);
        auto stop = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
        if (duration.count() > 300) {
            BOOST_LOG_TRIVIAL(error) << "Sent broadcast to " << i << " in " << duration.count() << " milliseconds";
        }

    }
    auto stop_cycle = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(stop_cycle - start_cycle);
    BOOST_LOG_TRIVIAL(debug) << "Completed broadcast cycle in " << duration.count() << " milliseconds";
}

当我不设置套接字选项,而是依赖Linux配置时,输出:

代码语言:javascript
运行
复制
 [ RUN      ] UDPSenderTest.Broadcast
37: [2022-10-06 09:56:08.183035] [0x00007f5f15f8b740] [debug]   Send buffer size (SO_SNDBUF) is: 212992
37: [2022-10-06 09:56:11.256173] [0x00007f5f15f8b740] [debug]   Sent broadcast to 247 in milliseconds 3069
37: [2022-10-06 09:56:11.256590] [0x00007f5f15f8b740] [debug]   Completed broadcast cycle in 3073 milliseconds
EN

Stack Overflow用户

回答已采纳

发布于 2022-10-07 20:02:08

您设置了错误的内核参数。

net.core.rmem_defaultnet.core.rmem_max参数分别设置UDP 接收缓冲区的默认大小和最大大小。UDP 发送缓冲区的相应参数是net.core.wmem_defaultnet.core.wmem_max

通常,为您的程序设置UDP发送缓冲区的正确方法是使用SO_SNDBUF套接字选项,您设置的值将受到net.core.wmem_max内核参数的限制。然后,如果需要一个大于最大值的值,则可以增加net.core.wmem_max的值。

您通常不希望修改net.core.wmem_defaultnet.core.rmem_default,因为这将影响系统上的其他进程。

票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73976502

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档