c++ - 服务器和客户端都运行在 PC 上。客户端可以使用PC的IP连接到服务器吗?
问题描述
我是网络编程的新手,所以这让我困惑了好几天。
我写了 2 个应用程序:一个是服务器,一个是客户端。
我希望他们做的是:当我为客户端提供 IP 地址时,客户端将使用提供的 IP 连接到机器上的服务器。
我通过在我的 PC 上打开服务器和客户端并输入环回地址 (127.0.0.1) 来测试它。客户端连接到服务器很好(我什至可以互相发送短信)。
所以我进一步尝试。我用谷歌搜索了我的 IP(即 113.20.98.124),然后将其提供给我的客户。我预计客户端也会连接到服务器,但这并没有发生。客户端终止并说连接超时(WSAETIMEDOUT 10060)。
我想问的是:为什么我不能用我的IP告诉客户端连接到我的服务器?这甚至是一回事吗?
这是我的服务器和客户端代码(以防我做错了):
(我在 Windows 上使用 VS2017 进行编码)
客户:
#include <iostream>
#include <WS2tcpip.h>
#include <winsock2.h>
#include <string>
//The port which will be used to connect to server
#define PORT "7777"
std::string getIPAddress()
{
std::string out;
std::cout << "IP/Domain to connect to: ";
std::cin >> out;
std::cin.ignore();
return out;
}
//Print out error code and exit the program in case something goes wrong
void failure_exit(std::string message, int exitVal)
{
std::cerr << message
<< " Error code: " << WSAGetLastError() << " \n";
WSACleanup();
std::cin.get();
exit(exitVal);
}
int main(int argc, char *argv[])
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 0), &wsaData) != 0)
failure_exit("WSAStartup failed!", 1);
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; //AF_INET or AF_INET6 are totally fine
hints.ai_socktype = SOCK_STREAM; //TCP connection
//Get the IP from user
std::string IPconnect = getIPAddress();
//A pointer to a linked list of juicy results we can use
addrinfo *result(nullptr);
//Prepare the addrinfo for a connecting socket
if (getaddrinfo(IPconnect.c_str(), PORT, &hints, &result) != 0)
failure_exit("getaddrinfo failed!", 2);
/*PREPARING A SOCKET*/
SOCKET sockfd;
//A pointer to one of the addrinfos from result, which is a usable one
//'chosen' will also be used in connect() call
addrinfo *chosen(result);
//Run through the results got from getaddrinfo and pick the first usable addrinfo
for (; chosen != nullptr; chosen = chosen->ai_next)
{
//see if sockfd is legit to use
if ((sockfd = socket(chosen->ai_family, chosen->ai_socktype, chosen->ai_protocol)) != -1)
break;
}
freeaddrinfo(result);
//Socket preparation failed
if (sockfd<=0)
failure_exit("Socket preparation failed!", 3);
/*CONNECT!*/
std::cout << "Connecting... 20 seconds until timed out.\n";
if (connect(sockfd, chosen->ai_addr, chosen->ai_addrlen) != 0)
failure_exit("Failed to connect!", 4);
std::cout << "Connected!\n";
WSACleanup();
std::cin.get();
return 0;
}
服务器:
#include <iostream>
#include <WS2tcpip.h>
#include <winsock2.h>
#include <string>
//The port which this server will be using
//to listen to incoming connections
#define PORT "7777"
//Limits how many pending connections
//can be queued up
#define BACKLOG 10
void failure_exit(std::string message, int exitVal)
{
std::cerr << message
<< " Error code: " << WSAGetLastError() << " \n";
WSACleanup();
std::cin.get();
exit(exitVal);
}
int main(int argc, char* argv[])
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(1, 0), &wsa) != 0)
failure_exit("WSAStartup failed!", 1);
addrinfo hints;
memset(&hints, 0, sizeof(hints));
//IPv4 or IPv6 are both Ok
hints.ai_family = AF_INET;
//This addrinfo will be used for binding
hints.ai_flags = AI_PASSIVE;
//TCP connection
hints.ai_socktype = SOCK_STREAM;
addrinfo *result;
if (getaddrinfo(NULL, PORT, &hints, &result) != 0)
failure_exit("getaddrinfo failed!", 3);
//PREPARE A SOCKET
SOCKET sockfd(0);
//The usable addrinfo will be pointed to by 'chosen'
addrinfo *chosen(result);
//Loop through the results to find a suitable one
for (; chosen != nullptr; chosen = chosen->ai_next)
{
sockfd = socket(chosen->ai_family, chosen->ai_socktype, chosen->ai_protocol);
//Stop at the first usable
if (sockfd != -1)
break;
}
freeaddrinfo(result);
//Check for preparation failure
if (sockfd <= 0)
failure_exit("Socket preparation failed!", 4);
//Bind the socket above to my chosen PORT
if (bind(sockfd, chosen->ai_addr, chosen->ai_addrlen) == -1)
failure_exit("Binding failed!", 5);
//Start listening for incoming connections
if (listen(sockfd, BACKLOG) == -1)
failure_exit("Listening failed!", 6);
//The new socket to be returned by accept()
SOCKET newfd;
sockaddr_storage newConnection;
socklen_t newlength(sizeof(newConnection));
std::cout << "Anyone?\n";
//Accept a pending connection
if ((newfd = accept(sockfd, reinterpret_cast<sockaddr*>(&newConnection), &newlength)) == -1)
failure_exit("Accepting connection failed!", 7);
std::cout << "Connection accepted!\n";
WSACleanup();
std::cin.get();
return 0;
}
我学到的任何关于网络编程的知识都来自http://beej.us/guide/。
解决方案
当你用谷歌搜索你的 IP 时,你得到了外界所看到的公共 IP。如果您的 PC直接连接到 Internet 调制解调器,则该 IP 属于您的 PC。但是,如果您的 PC 位于网络路由器后面(这在如今拥有许多联网设备的家庭中很常见),那么该 IP 属于您的网络路由器,而不是您的 PC。
您的服务器应用程序只能侦听已直接分配给运行它的 PC 的本地/LAN IP。如果那台 PC 在网络路由器后面运行,它就无法侦听路由器的公共 IP。
您的服务器代码正在绑定其侦听套接字,以便侦听分配给正在运行的 PC 的所有可用 IPv4 IP。要发现这些 IP 实际是什么,您可以使用 Windows 的命令行ipconfig
工具。或者,在您的服务器代码中,您可以使用GetAdaptersInfo()
或GetAdaptersAddresses()
API。
如果您的客户端与服务器应用程序在同一 PC/网络上运行,它可以直接连接到服务器正在侦听的任何本地/LAN IP。但是,如果客户端在不同的网络上运行(即,在 Internet 上的另一台 PC 上),它需要连接到您的公共 IP。并且在该公共 IP 属于网络路由器的情况下,需要将路由器配置为将给定的入站连接转发<PublicIP>:<PublicPort>
到服务器 PC <LanIP:LanPort>
(即,转发113.20.98.124:777
到类似的东西192.168.0.1:777
,或实际分配给服务器 PC 的任何 IP )。
推荐阅读
- apache - 使用 htaccess 获取带有和不带有斜杠的漂亮 URL?
- react-native - 将 sdk 38 更新到 39 时,使用 expo react native 执行应用程序时出错
- java - 编译器给出“错误:找不到符号”消息。我认为这与方法有关
- python - 正方形不对齐 - 多边形和圆形
- c++ - 在 C++ 11 中使用 decltype 和模板进行矩阵加法
- html - 如何使用引导列/网格系统将按钮放在输入框旁边?
- python-3.x - Python - Error500 尝试使用请求发布表单(内容类型:多部分/表单数据)
- ios - 金属片段着色器 - 如何根据可以通过绘图功能调整的制服为每个像素分配颜色?
- python - 我如何计算每列和每行的平均值并沿 10*3 数组打印
- laravel - 在 AWS Elastic Beanstalk 上安装 composer 时出错