linux网络编程:构建高性能、低延迟的 UDP 网络应用

本文转自https://zhuanlan.zhihu.com/p/18040389193

1.SO_REUSEPORT 和 UDP 多路复用

SO_REUSEPORT是一种允许多个进程或线程绑定同一个端口的套接字选项,它可以用于 UDP 的负载均衡。当多个进程或线程监听同一个端口时,内核会自动将传入的数据包分发到这些进程或线程中,从而实现负载均衡。这对于多核 CPU 系统尤为重要,因为它能够充分利用多核的并行处理能力。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    // 启用 SO_REUSEPORT
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        return -1;
    }
    return 0;
}

2. UDP 收发性能优化:Zero-Copy 技术

Linux 提供了一些零拷贝(Zero-Copy)技术,这些技术不仅能用于 TCP,也同样适用于 UDP。通过这些技术,可以避免多次数据拷贝,提高网络吞吐量和性能。

sendfile():虽然 sendfile() 主要用于文件传输,但它也可以与 UDP 配合使用,实现零拷贝传输数据。这对于需要传输大量数据的场景尤为有效。

splice() 和 vmsplice():通过这些系统调用,应用程序可以避免将数据从内核空间复制到用户空间,从而减少数据复制的开销,提高数据的传输效率。

splice() 进行 UDP 数据传输

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>

int udp_zero_copy_send(int udp_socket, int file_fd, struct sockaddr_in *dest_addr) {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return -1;
    }

    // 将文件数据通过 splice 写入 pipe
    if (splice(file_fd, NULL, pipefd[1], NULL, 4096, SPLICE_F_MORE) == -1) {
        perror("splice");
        return -1;
    }

    // 从 pipe 中读取数据并通过 UDP 发送
    char buffer[4096];
    ssize_t len;
    while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
        sendto(udp_socket, buffer, len, 0, (struct sockaddr *)dest_addr, sizeof(*dest_addr));
    }

    close(pipefd[0]);
    close(pipefd[1]);
    return 0;
}

3. UDP-Lite(用户数据报轻量级协议)

UDP-Lite 是一种类似于标准 UDP 的协议,但与 UDP 不同的是,它支持部分数据的完整性检查。在 UDP 中,如果数据包的某部分丢失,整个数据包就会被丢弃,而 UDP-Lite 允许用户定义哪些部分的数据是需要进行校验的,从而实现更灵活的数据完整性控制。UDP-Lite 适用于语音和视频流等对部分丢失容忍较高的应用场景。

要启用 UDP-Lite,应用程序只需在创建 UDP 套接字时指定 SOCK_DGRAM 和 IPPROTO_UDPLITE即可。

int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);

4. Multicast(多播)支持

Linux 内核对 UDP 多播(Multicast)的支持非常强大。通过 UDP 多播,一个数据包可以同时传输到多个接收者。应用程序可以利用 setsockopt() 设置多播相关的选项来发送和接收多播数据。
配置多播:
1.发送多播数据

struct sockaddr_in dest_addr;
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

// 配置目标地址
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(12345);
inet_pton(AF_INET, "224.0.0.1", &dest_addr.sin_addr);

// 发送数据
sendto(sockfd, "Hello, Multicast!", 18, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));

2.接收多播数据

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in local_addr;
struct ip_mreq mreq;

// 配置接收多播数据的地址
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = INADDR_ANY;
local_addr.sin_port = htons(12345);
bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr));

// 加入多播组
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

// 接收多播数据
char buffer[1024];
recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);

5. UDP Performance Tuning:调整缓冲区大小和批量发送

Linux 允许开发者通过 setsockopt() 调整 UDP 套接字的发送和接收缓冲区大小,这对提高 UDP 的吞吐量非常有帮助。

调整缓冲区大小:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

// 设置接收缓冲区大小
int recv_buf_size = 256 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, sizeof(recv_buf_size));

// 设置发送缓冲区大小
int send_buf_size = 256 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &send_buf_size, sizeof(send_buf_size));

此外,Linux 还允许对 UDP 数据包的发送进行批量处理,通过合并多个 UDP 数据包一起发送,可以减少网络拥塞并提高吞吐量。

  1. UDP 与 io_uring 结合
    io_uring 是 Linux 提供的一种高效的异步 I/O 机制,它大幅降低了内核与用户空间之间的上下文切换开销,并能够实现高效的 I/O 操作。虽然 io_uring 最初主要针对块设备和文件系统的 I/O,但它也支持 UDP 的异步操作。通过 io_uring,UDP 套接字的读写操作可以变得更加高效,尤其是在高并发场景下。

使用 io_uring 进行 UDP 异步操作头文件

#include <liburing.h>

总结

在 Linux 下的 UDP 编程,除了传统的 socket API 外,新的技术和工具为高效的网络通信提供了更强大的支持。例如,SO_REUSEPORT 实现的负载均衡、多播支持、零拷贝技术、io_uring 异步 I/O 等都大大提高了 UDP 编程的性能和灵活性。通过合理利用这些新技术,开发者可以更加高效地构建高性能、低延迟的 UDP 网络应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值