C ++ Winsock2 Client не подключается к серверу через удаленный IP - PullRequest
0 голосов
/ 16 сентября 2018

Я пытаюсь изучить основы сетевого программирования с помощью Winsock2 API. Мне удалось подключиться через IP-адреса локальной сети, но я уже целый день пытаюсь заставить клиента подключиться к моему серверу через его общедоступный IP-адрес.

Я уже настроил переадресацию портов на своем маршрутизаторе и даже использовал Wireshark для отслеживания запросов на подключение клиентов. Я вижу запросы в Wireshark, но он никогда не подключается к серверу, и в итоге я получаю сообщение об ошибке тайм-аута.

Я в растерянности, я ценю любого, кто может указать мне правильное направление!

Это реализация клиента:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")



#define DEFAULT_PORT //MY PORT
#define DEFAULT_BUFLEN 512
#define SERVER_IPV4 //"MY PUBLIC IP STRING"


int main(int argc, char **argv)
{

    //wsaData to hold Winsock dll information
    WSADATA wsaData;
    WORD wVersionRequired = MAKEWORD(2, 2);

    //Attempts to load winsock dll matching required version and fills WSADATA object
    int wsaInit = WSAStartup(wVersionRequired, &wsaData);
    if(wsaInit != 0)
    {
        printf("WSAStartup failed with error code: %d\n", wsaInit);
    }

    //If dll fails to load correct version free winsock dll resources
    if(wsaData.wHighVersion != wVersionRequired)
    {
        printf("No usable version of Winsock.dll found\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("Winsock dll 2.2 loaded correctly\n");
    }


    /**********************Socket Code Here**********************/

    SOCKADDR_IN SockAddrIP4;
    SockAddrIP4.sin_family = AF_INET;
    SockAddrIP4.sin_addr.s_addr = inet_addr(SERVER_IPV4);
    SockAddrIP4.sin_port = htons(DEFAULT_PORT);

    /**************Create Socket****************/

    //INVALID_SOCKET used like NULL
    SOCKET ConnectSocket = INVALID_SOCKET;

    // TODO(baruch): Only supporting IP_V4
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(ConnectSocket == INVALID_SOCKET)
    {
        printf("socket() error: %ld\n", WSAGetLastError());
        //clean up address info after getaddrinfo function when socket fails
        WSACleanup();
        return 1;
    }

    /*****************Connect to socket**************/

    int connectResult = connect(ConnectSocket, (SOCKADDR*)&SockAddrIP4, sizeof(SOCKADDR_IN));
    if(connectResult == SOCKET_ERROR)
    {
        printf("Connect failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        ConnectSocket = INVALID_SOCKET;
    }
    else
    {
        printf("Connected with server: %s\n", SERVER_IPV4);
    }

    if(ConnectSocket == INVALID_SOCKET)
    {
        printf("Unable to connect with server\n");
        WSACleanup();
        return 1;
    }

    /*************END of Socket CODE CLEANUP********/

    // TODO(baruch): close socket
    WSACleanup();

}

А это сервер:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

// TODO(baruch): Only supporting ascii consider Unicode later
#undef UNICODE

#define DEFAULT_PORT //My Port
#define DEFAULT_BUFLEN 512

int main(int argc, char **argv)
{

    //wsaData to hold Winsock dll information
    WSADATA wsaData;
    WORD wVersionRequired = MAKEWORD(2, 2);


    int wsaInit = WSAStartup(wVersionRequired, &wsaData);
    if(wsaInit != 0)
    {
        printf("WSAStartup failed with error code: %d\n", wsaInit);
    }

    //If dll fails to load correct version free winsock dll resources
    if(wsaData.wHighVersion != wVersionRequired)
    {
        printf("No usable version of Winsock.dll found\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("Winsock dll 2.2 loaded correctly\n");
    }

    /**********************Socket Code Here**********************/


    /**************Create Socket****************/
    SOCKADDR_IN SockAddrIP4;
    SockAddrIP4.sin_family = AF_INET;
    SockAddrIP4.sin_addr.s_addr = INADDR_ANY;
    SockAddrIP4.sin_port = htons(DEFAULT_PORT);

    //INVALID_SOCKET used like NULL
    SOCKET ListenSocket = INVALID_SOCKET;

    // TODO(baruch): Only supporting IP_V4
    // Socket for server to listen on for client connections
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(ListenSocket == INVALID_SOCKET)
    {
        printf("socket() error: %ld\n", WSAGetLastError());
        //clean up address info after getaddrinfo function when socket fails
        WSACleanup();
        return 1;
    }

    /**************Bind Socket******************/
    int bindResult = bind(ListenSocket, (SOCKADDR*)&SockAddrIP4, sizeof(SOCKADDR_IN));
    if(bindResult == SOCKET_ERROR)
    {
        printf("failed to bind with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    /************Listen for Connections**********/
    int listenResult = listen(ListenSocket, SOMAXCONN);
    if(listenResult == SOCKET_ERROR)
    {
        printf("Listen failed, error: %d\n", WSAGetLastError() );
        WSACleanup();
        return 1;
    }
    else
    {
        printf("Now listening for client connections...\n");
    }
    /************Accept and Handle CLient Connections***********/
    // TODO(baruch): For testing, only allowing a single client. Eventually need to create a loop to handle all client connections.
    SOCKET ClientSocket;
    SOCKADDR_IN connectedAddress;
    int addressLength = sizeof(connectedAddress);
    ClientSocket = accept(ListenSocket, (SOCKADDR *) &connectedAddress, &addressLength);
    if(ClientSocket == SOCKET_ERROR)
    {
        printf("Accept failed, error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    else
    {
        //inet_ntoa converts ip address to binary format.
        char *clientIp = inet_ntoa(connectedAddress.sin_addr);
        // TODO(baruch): Make sure this string correctly prints address
        printf("Client connection from: %s accepted\n", clientIp);
    }

    /**********Handle inbound and outbound data**********/

    char inBuf[DEFAULT_BUFLEN];
    int dataBufLen = DEFAULT_BUFLEN;
    int inDataResult, outDataResult;

    do
    {
        inDataResult = recv(ClientSocket, inBuf, dataBufLen, 0);
        if(inDataResult > 0)
        {
            printf("Number of bytes received: %d", inDataResult);

            //Confirm to client message received
            char confirmReceipt[] = "\nMessage received!\n";
            outDataResult =
                send(ClientSocket, confirmReceipt, sizeof(confirmReceipt), 0);
            if(outDataResult == SOCKET_ERROR)
            {
                printf("Confirmation message failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
            else
            {
                printf("Confirmation message sent\n");
            }
        }
        else if(inDataResult == 0)
        {
            printf("Connection closing\n");
        }
        else
        {
            printf("Data receipt failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

    } while(inDataResult > 0);

    //Shutdown sending portion of socket, can still receive data
    int shutDownResult = shutdown(ClientSocket, SD_SEND);
    if(shutDownResult == SOCKET_ERROR)
    {
        printf("Shutdown failure, error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }


    /*************End Socket Code Clean up Winsock dll**********/

    closesocket(ClientSocket);
    WSACleanup();
}

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Благодаря Селби я смог пройти процедуру исключения и определить, что с моим сервисом что-то не так.Я связался со своим провайдером, и ему пришлось сменить тип сервиса и дать мне реальный публичный IP.По умолчанию они используют NAT операторского уровня, что означает, что жилым сайтам назначается частный IP-адрес, который транслируется в общедоступный IP-адрес «транслятором сетевого адреса промежуточного ящика» где-то в сети интернет-провайдера.Спасибо!

0 голосов
/ 16 сентября 2018

Если вы можете подключиться через локальный IP-адрес локальной сети, но не через публичный IP-адрес, вероятно, это одна из этих проблем.

  1. Включили ли вы вашу программу через брандмауэр Windows? Полностью выключите брандмауэр Windows (временно), чтобы убедиться.

  2. Если и ваш клиент, и сервер находятся за одним и тем же NAT, ваш NAT может не разрешить подключению клиентов через публичный IP-адрес. Это называется NAT шпилька . Не все NAT поддерживают это. Подтвердите, что вы можете подключиться к IP-адресу вашего сервера через клиента за пределами сети вашего сервера.

  3. В случае сомнений используйте простую программу, например netcat , для проверки возможности подключения сокетов между ПК. Простой тест состоит в том, чтобы просто запустить nc в режиме прослушивания вашего порта, а затем использовать другой экземпляр nc для подключения к нему. Выполните поиск в Интернете для «Netcat для Windows». Если вы можете подключиться к вашему порту через netcat, но не через свой клиент-серверный код, то проблема с вашим кодом. Если вы не можете подключиться к порту через netcat, это ошибка брандмауэра или сетевой конфигурации.

...