首页 > 系统相关 >linux socket 多个 socket 复用同一个 ip 和 port

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

时间:2024-05-14 11:11:36浏览次数:26  
标签:socket ip address sock server SO linux fd

在 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

相关文章

  • linux安装cuda和cudnn
    //安装cudawgethttps://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pinsudomvcuda-wsl-ubuntu.pin/etc/apt/preferences.d/cuda-repository-pin-600wgethttps://developer.download.nvidia.com/compute/cuda/12.4.1/local_ins......
  • Linux上执行内存中的脚本和程序
    在Linux中可以不需要有脚本或者二进制程序的文件在文件系统上实际存在,只需要有对应的数据在内存中,就有办法执行这些脚本和程序。原理其实很简单,Linux里有办法把某块内存映射成文件描述符,对于每一个文件描述符,Linux会在/proc/self/fd/<文件描述符>这个路径上创建一个对应描述符的......
  • macOS Sonoma 14.5 (23F79) 正式版发布,ISO、IPSW、PKG 下载
    macOSSonoma14.5(23F79)正式版发布,ISO、IPSW、PKG下载2024年5月14日凌晨,macOSSonoma14.5发布,本更新提供了重要的错误修复,建议所有用户安装。随着版本14.5的发布,macOSSonoma的更新使命已基本完成,不出意外此版本将成为推荐版本,适合所有Mac全新安装。期待WWDC......
  • 2 用电信号传输TCP/IP数据
    目录1创建套接字2连接服务器3收发数据1创建套接字浏览器、邮件等一般应用程序收发数据时用TCP;DNS查询等收发较短的控制数据时用UDP网络包:网络中的数据会被切分成几十字节到几千字节的小块,每一个小数据块被称为一个包IP中还包括ICMPA协议和ARPB协议。ICMP用......
  • 记录一次Windows上简单向linux上传文件
    1直接账号密码登录上传---使用winscp(得先安装winscp)``@echooffREM设置WinSCP安装路径setWINSCP_PATH="C:\ProgramFiles(x86)\WinSCP\WinSCP.com"REM设置连接参数setHOSTNAME=192.168.1.112setUSERNAME=rootsetPASSWORD=xxxxxxxxsetREMOTE_PATH=/home/......
  • skipped: maximum number of running instances reached (1)
    Python的 apscheduler今天出现skipped:maximumnumberofrunninginstancesreached(1)问题产生的原因:设置了大量的任务,而APScheduler无法同时处理所有任务解决方法:调整APScheduler使用的线程池大小来增加并发处理任务的能力fromapscheduler.schedulers......
  • [题解] Flipping Game
    题目描述有n盏灯,每个灯有开和关两种状态。每按一次灯会变成相反的状态。给定灯初始的开关状态和结束的开关状态,若操作k轮,每轮要按m个不同的灯,问有多少种方法使灯由初始状态变到结束状态。题解需注意每轮要按不同的灯,若无这个条件,暴力计算组合数即可。要操作多轮,且每轮按灯的......
  • 随笔-网络-Linux多网卡同网段解决方法(配置IP路由)
    系统配置:多网口情况下,假设各个网口ip在同一网段;此时,由于默认路由规则,从不同网口的ip访问,服务器依旧会返回默认网口的mac地址,配置方式如下:sysctl-wnet.ipv4.ip_forward=1#开启ip转发规则net.ipv4.conf.X.rp_filter=0#X填alldefault各个网卡路由设置:假设默认网卡......
  • linux里安装sql2022详细步骤
    https://learn.microsoft.com/zh-tw/sql/linux/quickstart-install-connect-ubuntu?view=sql-server-linux-ver16&preserve-view=true&tabs=ubuntu2004https://learn.microsoft.com/zh-tw/sql/linux/quickstart-install-connect-ubuntu?view=sql-server-linux-ver16&a......
  • 运维必备Linux学习day2(mysql,jdk,redis,docker安装)
    一.MySQL安装①Linux环境:1.虚拟机Centos7.6版本安装,2.准备类似版本 mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar包1.新建文件夹/opt/mysql,并cd进去,首先:mkdir/opt/mysql2.运行 wgethttp://dev.mysql.com/get/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar,下载mysql安装包......