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

时间:2017-08-30 05:59:02

标签: networking tcp network-programming port tcp-ip

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

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

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

1 个答案:

答案 0 :(得分:1)

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

man socket(7)

  

SO_REUSEPORT (自Linux 3.9开始)

     

允许将多个AF_INETAF_INET6套接字绑定到相同的套接字地址。在调用套接字上的bind(2)之前,必须在每个套接字(包括第一个套接字)上设置此选项。为防止端口劫持,绑定到同一地址的所有进程必须具有相同的有效UID。此选项可与TCP和UDP套接字一起使用。

这是一个工作示例,它有两个套接字绑定到同一个地址和端口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连接到相同的:addr port。