linux socket 多个 socket 复用同一个 ip 和 port

在 Linux 系统中,对于 UDP socket,与 TCP socket 相比,其行为略有不同。UDP 是无连接的协议,通常不需要建立连接,因此它的端口复用规则也有所不同。

默认情况下,每个 UDP socket 绑定到一个唯一的 IP 地址和端口组合。然而,从 Linux 内核版本 3.9 开始,引入了 SO_REUSEADDR 和 SO_REUSEPORT 套接字选项,这些选项允许更灵活的绑定行为。

SO_REUSEADDR:这个选项允许多个 socket 绑定到同一个 IP 地址和端口,但对于 UDP socket,它通常只允许一个 socket 绑定到一个特定的地址和端口,除非该 socket 已经设置了 SO_REUSEADDR。即使设置了 SO_REUSEADDR,不同的 socket 仍然不能绑定到完全相同的地址和端口,除非它们属于同一个进程。

SO_REUSEPORT:这个选项允许完全的端口复用,即多个 socket 可以绑定到同一个 IP 地址和端口。当使用 SO_REUSEPORT 时,每个 socket 将独立地接收发送到该地址和端口的数据包。这对于多线程或多进程的应用程序来说非常有用,因为它可以提高性能,允许操作系统在不同的 socket 之间分配传入的 UDP 数据包。

要使用 SO_REUSEPORT,你需要在 socket 选项中设置它,如下所示:

int optval = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
在设置了 SO_REUSEPORT 之后,你可以安全地绑定多个 socket 到同一个 IP 地址和端口,如下所示:

bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
请注意,即使使用了 SO_REUSEPORT,每个 socket 仍然需要调用 bind() 来绑定到特定的地址和端口,但是操作系统将允许多个 socket 绑定到相同的地址和端口,并且每个 socket 将独立地接收数据包。

总结来说,在 Linux 上,同一个进程可以使用 SO_REUSEPORT 选项来让多个 UDP socket 绑定到同一个 IP 地址和端口。这对于需要多个处理线程或进程的高并发 UDP 服务器非常有用。

 

##service.cpp

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>
#include <thread>
 
#define PORT1 55100  
#define PORT2 55200
#define BUF_SIZE 1024  


void test_thread1()
{
    int server_fd, new_socket;  
    struct sockaddr_in address;  
    int addrlen = sizeof(address);  
    char buffer[BUF_SIZE] = {0};  
 
    // 1. 创建套接字  
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }  

    int optval = 1;  
    // if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {  
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");  
        exit(EXIT_FAILURE);  
    }
 
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(PORT1);  
 
    // 2. 绑定套接字  
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
 
    // 3. 监听连接  
    if (listen(server_fd, 3) < 0) {  
        perror("listen");  
        exit(EXIT_FAILURE);  
    }  
 
    // 4. 接受客户端连接  
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {  
        perror("accept");  
        exit(EXIT_FAILURE);  
    }  
 
    // 5. 发送和接收数据  
    read(new_socket, buffer, BUF_SIZE - 1);  
    printf("Received from client: %s\n", buffer);  
    send(new_socket, "Hello from server-1!", strlen("Hello from server-1!"), 0);  
 
    // 6. 关闭套接字  
    close(new_socket);  
    close(server_fd);  
}

void test_thread2()
{
    int server_fd, new_socket;  
    struct sockaddr_in address;  
    int addrlen = sizeof(address);  
    char buffer[BUF_SIZE] = {0};  
 
    // 1. 创建套接字  
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }

    int optval = 1;  
    // if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {  
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");  
        exit(EXIT_FAILURE);  
    }
 
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(PORT1);  
 
    // 2. 绑定套接字  
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
 
    // 3. 监听连接  
    if (listen(server_fd, 3) < 0) {  
        perror("listen");  
        exit(EXIT_FAILURE);  
    }  
 
    // 4. 接受客户端连接  
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {  
        perror("accept");  
        exit(EXIT_FAILURE);  
    }  
 
    // 5. 发送和接收数据  
    read(new_socket, buffer, BUF_SIZE - 1);  
    printf("Received from client: %s\n", buffer);  
    send(new_socket, "Hello from server-2!", strlen("Hello from server-2!"), 0);  
 
    // 6. 关闭套接字  
    close(new_socket);  
    close(server_fd);  
}

int main() {  
    std::thread task1(test_thread1);
    std::thread task2(test_thread2);

    task1.join();
    task2.join();
    return 0;
}

 

##client.cpp

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <thread>
 
#define PORT1 55100  
#define PORT2 55200
#define SERVER_IP "127.0.0.1"  
#define BUF_SIZE 1024  

int test_thread1()
{
    int sock;  
    struct sockaddr_in server;  
    char message[100] = "Hello from client-1!";  
    char server_reply[BUF_SIZE] = {0};  
 
    // 1. 创建套接字  
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        printf("\n Socket creation error \n");  
        return -1;  
    }  
 
    server.sin_addr.s_addr = inet_addr(SERVER_IP);  
    server.sin_family = AF_INET;  
    server.sin_port = htons(PORT1);  
 
    // 2. 连接到服务端  
    if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {  
        perror("connect failed. Error");  
        return 1;  
    }  
 
    // 3. 发送和接收数据  
    send(sock, message, strlen(message), 0);  
    read(sock, server_reply, BUF_SIZE - 1);  
    printf("Server reply-1: %s\n", server_reply);  
 
    // 4. 关闭套接字  
    close(sock);  
}

int test_thread2()
{
    int sock;  
    struct sockaddr_in server;  
    char message[100] = "Hello from client-2!";  
    char server_reply[BUF_SIZE] = {0};  
 
    // 1. 创建套接字  
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        printf("\n Socket creation error \n");  
        return -1;  
    }  
 
    server.sin_addr.s_addr = inet_addr(SERVER_IP);  
    server.sin_family = AF_INET;  
    server.sin_port = htons(PORT1);  
 
    // 2. 连接到服务端  
    if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {  
        perror("connect failed. Error");  
        return 1;  
    }  
 
    // 3. 发送和接收数据  
    send(sock, message, strlen(message), 0);  
    read(sock, server_reply, BUF_SIZE - 1);  
    printf("Server reply-2: %s\n", server_reply);  
 
    // 4. 关闭套接字  
    close(sock);  
}
 
int main() {  
    std::thread task1(test_thread1);
    std::thread task2(test_thread2);

    task1.join();
    task2.join();
 
    return 0;  
}

 

posted @ 2024-05-14 10:59  牛博张  阅读(265)  评论(0编辑  收藏  举报