C ++ Winsock2客户端未通过远程IP连接到服务器

C++ Winsock2 Client not connecting to server through remote IP

我正在尝试使用 Winsock2 API 学习网络编程的基础知识。我已经通过 LAN IP 地址成功连接,但我已经努力了一天多,试图让客户端通过它的 public 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();
}

如果您可以通过本地 LAN IP 地址连接,但不能通过 public IP 地址连接,则可能是这些问题之一。

  1. 您是否允许您的程序通过 Windows 防火墙?完全关闭 Windows 防火墙(暂时)只是为了确保。

  2. 如果您的客户端和服务器都位于同一个 NAT 之后,您的 NAT 可能不允许客户端连接通过 public IP 地址进行连接。这叫做NAT hairpinning。并非所有 NAT 都支持这一点。验证您是否可以通过服务器网络外部的客户端连接到服务器的 IP 地址。

  3. 如有疑问,请使用像 netcat 这样的简单程序来测试 PC 之间的套接字连接。简单的测试是 运行 nc 在您的端口上处于侦听模式,然后使用另一个 nc 实例连接到它。在 Internet 上搜索 "Netcat for Windows"。如果您可以通过 netcat 连接到您的端口,但不能通过您的 client/server 代码连接到您的端口,那么问题出在您的代码上。如果你不能通过netcat连接到你的端口,那就是防火​​墙或网络配置错误。

多亏了 Selbie,我才能够完成排除过程并确定我的服务有问题。我联系了我的 ISP,他们不得不更改服务类型并给我一个真实的 public IP。默认情况下,他们使用运营商级 NAT,这意味着住宅站点被分配了一个私有 IP,该 IP 由 ISP 网络中的某个 "middlebox network address translator" 转换为 public IP。 谢谢!