在 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; }
标签:socket,ip,address,sock,server,SO,linux,fd From: https://www.cnblogs.com/liangzige/p/18190878