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

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

本文关键字:IP 连接 服务器 Winsock2 客户端 C++      更新时间:2023-10-16

我正在尝试通过使用Winsock2 API来学习网络编程的基础知识。我已经成功地通过 LAN 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: %dn", wsaInit);
}
//If dll fails to load correct version free winsock dll resources
if(wsaData.wHighVersion != wVersionRequired)
{
printf("No usable version of Winsock.dll foundn");
WSACleanup();
return 1;
}
else
{
printf("Winsock dll 2.2 loaded correctlyn");
}

/**********************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: %ldn", 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: %dn", WSAGetLastError());
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
else
{
printf("Connected with server: %sn", SERVER_IPV4);
}
if(ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect with servern");
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: %dn", wsaInit);
}
//If dll fails to load correct version free winsock dll resources
if(wsaData.wHighVersion != wVersionRequired)
{
printf("No usable version of Winsock.dll foundn");
WSACleanup();
return 1;
}
else
{
printf("Winsock dll 2.2 loaded correctlyn");
}
/**********************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: %ldn", 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: %dn", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
/************Listen for Connections**********/
int listenResult = listen(ListenSocket, SOMAXCONN);
if(listenResult == SOCKET_ERROR)
{
printf("Listen failed, error: %dn", 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: %dn", 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 acceptedn", 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: %dn", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
else
{
printf("Confirmation message sentn");
}
}
else if(inDataResult == 0)
{
printf("Connection closingn");
}
else
{
printf("Data receipt failed with error: %dn", 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: %dn", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}

/*************End Socket Code Clean up Winsock dll**********/
closesocket(ClientSocket);
WSACleanup();
}

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

  1. 是否使程序能够通过 Windows 防火墙? 完全关闭Windows防火墙(暂时(只是为了确保。

  2. 如果客户端和服务器位于同一 NAT 后面,则 NAT 可能不允许客户端连接通过公共 IP 地址进行连接。 这称为 NAT 发夹。并非所有 NAT 都支持此功能。验证您是否可以通过服务器网络外部的客户端连接到服务器的 IP 地址。

  3. 如有疑问,请使用 netcat 等简单程序来测试 PC 之间的套接字连接。 简单的测试是在端口上以侦听模式运行 nc,然后使用 nc 的另一个实例连接到它。 在互联网上搜索"Netcat for Windows"。 如果您可以通过netcat连接到端口,但不能通过客户端/服务器代码连接到端口,那么代码就有问题。 如果无法通过 netcat 连接到端口,则可能是防火墙或网络配置错误。

多亏了塞尔比,我才能经历一个淘汰的过程,并确定我的服务有问题。我联系了我的ISP,他们不得不更改服务类型并给我一个真正的公共IP。默认情况下,它们使用运营商级 NAT,这意味着住宅站点被分配一个私有 IP,该 IP 由 ISP 网络中某处的"中间设备网络地址转换器"转换为公共 IP。 谢谢!