不同网络间的socket编程

Bor*_*bat 1 c++ sockets tcp hole-punching

我正在用 C++ 开发一个客户端 - 服务器程序,以将数据从一台计算机传输到另一台计算机。

一切正常,但现在我被要求让它在不同网络的计算机上工作。我到处搜索,但找不到可靠的解决方案。

我已经看到了 TCP 打孔解决方案,但似乎无法在任何地方找到如何在 C++ 中做到这一点。

我希望它像 teamviewer 一样工作,但没有中间服务器。全部以编程方式将我的客户端(在一台计算机上)连接到服务器(在不同网络中的另一台计算机上)。

#include "../include/ip_tunnel_ms_windows_20180815.h" 
#include "../include/message_processor_common_20190410.h"


SOCKET clientSocket;
int n = 0;
void IPTunnel::initialize(void)
{

    if (inputSignals.empty()) {
        printf("server%d\n", n++);
        if (!server()) {
            printf("Error opening server\n");
            ::exit(1);
        }
    }
    else {
        printf("client%d\n", n++);
        if (!client()) {
            printf("Error opening client\n");
            ::exit(1);
        }
    }
}


bool IPTunnel::runBlock(void)
{
    .....
    (transmit data)
    .....
    return true;
}

void IPTunnel::terminate(void) {
    closesocket(clientSocket);
    WSACleanup();
}

bool IPTunnel::server() {
    WSADATA wsData;
    WORD ver = MAKEWORD(2, 2);

    int wsOk = WSAStartup(ver, &wsData);
    if (wsOk != 0)
    {
        cerr << "Can't Initialize winsock! Quitting" << endl;
        return false;
    }

    SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
    if (listening == INVALID_SOCKET)
    {
        cerr << "Can't create a socket! Quitting" << endl;
        return false;
    }

    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = ntohs(tcpPort);
    //inet_pton(AF_INET, (PCSTR)remoteMachineIpAddress.c_str(), &hint.sin_addr.s_addr); // hint.sin_addr.S_un.S_addr = inet_addr(ipAddressServer.c_str());
    hint.sin_addr.S_un.S_addr = INADDR_ANY;


    if (::bind(listening, (sockaddr*)& hint, sizeof(hint)) < 0) {
        printf("\n ERROR on binding");
        return false;
    }

    if (listen(listening, SOMAXCONN) == -1) {
        printf("\n ERROR on binding");
        return false;
    }

    sockaddr_in client;
    int clientSize = sizeof(client);

    clientSocket = accept(listening, (sockaddr*)& client, &clientSize);

    char host[NI_MAXHOST];
    char service[NI_MAXSERV];

    ZeroMemory(host, NI_MAXHOST);
    ZeroMemory(service, NI_MAXSERV);

    if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
    {
        cout << host << " connected on port " << service << endl;
    }
    else
    {
        inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
        cout << host << " connected on port " <<
            ntohs(client.sin_port) << endl;
    }
    return true;
}

bool IPTunnel::client() {

    WSAData data;
    WORD ver = MAKEWORD(2, 2);
    int wsResult = WSAStartup(ver, &data);
    if (wsResult != 0)
    {
        cerr << "Can't start Winsock, Err #" << wsResult << endl;
        return false;
    }

    clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == INVALID_SOCKET)
    {
        cerr << "Can't create socket, Err #" << WSAGetLastError() << endl;
        WSACleanup();
        return false;
    }

    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(tcpPort);
    inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);

    int connResult = -2;
    while (connResult != 0 || numberOfTrials == 0) {
        connResult = connect(clientSocket, (sockaddr*)& hint, sizeof(hint));
        if (connResult == SOCKET_ERROR)
        {
            cerr << "Can't connect to server, Err #" << WSAGetLastError() << endl;
            cerr << "Waiting " << timeIntervalSeconds << " seconds." << endl;
        }

        Sleep(timeIntervalSeconds * 1000);
        ;
        if (--numberOfTrials == 0) {
            cerr << "Reached maximum number of attempts." << endl;
            ::exit(1);
        }
    }
    cout << "Connected!\n";
    return true;
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*nck 5

打孔定义包括:

两个客户端都发起到一个不受限制的服务器的连接

你说:

我希望它像 teamviewer 一样工作,但没有中间服务器

这在一般情况下是不可能的。

  • @Borbat因为防火墙通常配置为阻止未经请求的入站连接。或者公共 IP 地址可能与 NAT 后面的其他系统共享。在这两种常见情况下,系统能够毫无问题地进行出站连接,但入站连接需要网络配置。这就是为什么软件经常使用两个端点都可以与其建立出站连接的中间服务器。 (2认同)
  • @Borbat:是的,您可以实现打孔或 STUN 或类似技术。也许中央服务器可以基于预先存在的开源项目。 (2认同)