多线程端口复用选项SO_REUSEPORT

1.介绍

  SO_REUSEPORT 是一个套接字选项,主要解决多进程/多线程服务程序中端口冲突和性能瓶颈的问题。


  它的核心作用是允许多个套接字绑定到完全相同的IP地址和端口。内核会把收到的连接请求(TCP)或数据包(UDP)分散给这些套接字,实现负载均衡。


它与 SO_REUSEADDR 的关键区别:

· SO_REUSEADDR:主要解决重启服务时“Address already in use”的报错,允许复用系统中处于 TIME_WAIT 状态的端口。
· SO_REUSEPORT:解决多个进程同时运行时的绑定问题,让它们同时、平等地监听同一端口。


2.使用它的主要好处是:


1. 消除“惊群”问题:传统多进程监听同一端口,一个新连接会唤醒所有进程去争抢。而使用 SO_REUSEPORT 后,内核会直接精准唤醒一个进程。

2. 提升性能:内核在内核态就完成了请求的分发,避免了用户态锁争抢,对UDP和短连接场景提升尤其明显。

3. 支持热升级:可以启动新版本进程绑定同一端口,再优雅关闭旧进程,实现零停机更新。


在 Linux 3.9 及以上版本,Nginx(1.9.1+)的 reuseport 参数、Redis 等主流软件都已支持此功能。


3.示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define BACKLOG 10

void handle_client(int client_fd) {
    char buf[1024];
    int n = read(client_fd, buf, sizeof(buf) - 1);
    if (n > 0) {
        buf[n] = '\0';
        printf("Process %d received: %s\n", getpid(), buf);
        const char *resp = "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK";
        write(client_fd, resp, strlen(resp));
    }
    close(client_fd);
}

void run_server() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket");
        exit(1);
    }

    // 关键:设置 SO_REUSEPORT
    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
        perror("setsockopt(SO_REUSEPORT)");
        exit(1);
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    if (listen(server_fd, BACKLOG) < 0) {
        perror("listen");
        exit(1);
    }

    printf("Process %d listening on port %d\n", getpid(), PORT);

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t len = sizeof(client_addr);
        int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
        if (client_fd < 0) {
            perror("accept");
            continue;
        }
        handle_client(client_fd);
    }
}

int main() {
    const int num_processes = 4;
    for (int i = 0; i < num_processes; ++i) {
        pid_t pid = fork();
        if (pid == 0) {   // 子进程
            run_server();
            exit(0);
        } else if (pid < 0) {
            perror("fork");
            exit(1);
        }
    }
    // 父进程等待,或者直接退出(让子进程在后台运行)
    for (int i = 0; i < num_processes; ++i) wait(NULL);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值