TCP 从已用于侦听的端口打开套接字

TCP open socket from port that's already used for listening

假设我有一个 TCP 服务器 (A) 侦听端口 8001。

现在,我想使用本地端口 8001 打开从服务器 (A) 到服务器 (B) 的 TCP 套接字。这样,服务器 (B) 将看到来自服务器 (A) 从端口 8001 的连接。

可能吗?我可以使用已经用于侦听传入连接的传出连接端口吗?

您可以这样做:socket,然后是 setsockopt(SO_REUSEPORT),然后是 bind

man socket(7):

SO_REUSEPORT (since Linux 3.9)

Permits multiple AF_INET or AF_INET6 sockets to be bound to an identical socket address. This option must be set on each socket (including the first socket) prior to calling bind(2) on the socket. To prevent port hijacking, all of the processes binding to the same address must have the same effective UID. This option can be employed with both TCP and UDP sockets.

这是一个工作示例,其中有两个套接字绑定到相同的地址和端口 127.0.0.1:2222。一个套接字是监听服务器套接字,另一个是成功连接到 127.0.0.1:22 (ssh):

的客户端
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <err.h>
#include <unistd.h>

int socket_and_bind() {
    int s = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == s)
        err(EXIT_FAILURE, "socket");

    int flag = 1;
    if(-1 == setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof flag))
        err(EXIT_FAILURE, "setsockopt(SO_REUSEPORT)");

    sockaddr_in sa = {};
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    sa.sin_port = htons(2222);
    sa.sin_family = AF_INET;
    if(-1 == bind(s, reinterpret_cast<sockaddr*>(&sa), sizeof sa))
        err(EXIT_FAILURE, "bind");

    return s;
}

void server(int s) {
    int c = accept(s, nullptr, nullptr);
    if(-1 == c)
        err(EXIT_FAILURE, "accept");
    close(c);
}

void client(int s) {
    sockaddr_in sa = {};
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    sa.sin_port = htons(22);
    sa.sin_family = AF_INET;

    if(-1 == connect(s, reinterpret_cast<sockaddr*>(&sa), sizeof sa))
        err(EXIT_FAILURE, "connect");

    char buf;
    if(1 != recv(s, &buf, sizeof buf, 0))
        err(EXIT_FAILURE, "recv");

    printf("connected\n");
}

int main() {
    int s1 = socket_and_bind();
    if(-1 == listen(s1, 1))
        err(EXIT_FAILURE, "listen");

    int s2 = socket_and_bind();

    std::thread t1(server, s1);
    std::thread t2(client, s2);
    t2.join();
    t1.detach();

    return EXIT_SUCCESS;
}

Linux 中的一个问题是从 addr:port 连接到 same:addr 端口。