Visual Studio 中的套接字 c++ 应用程序无法通过两台计算机之间的以太网连接工作

Socket c++ application in Visual Studio does not work over ethernet connection between two computers

本文关键字:两台 计算机 之间 工作 连接 以太网 套接字 Studio c++ 应用程序 Visual      更新时间:2023-10-16

我目前正在开发一个简单的服务器/客户端应用程序,使用Visual Studio中的C++通过以太网/LAN电缆连接将消息从一台计算机发送到另一台计算机。我正在使用我在网上找到的客户端和服务器的代码。

当我在同一台计算机上运行程序时,我可以从服务器接收消息。 但是,如果我在一台计算机上运行客户端程序并在另一台计算机上运行服务器程序,则不会收到任何消息。

由于我只是使用以太网电缆在两台计算机之间进行通信,因此我将 IP 地址(来自本地网络共享、适配器设置、TCP/IPv4)设置为特定于两台计算机,以便服务器计算机为 10.0.1.2,客户端计算机为 10.0.1.1,两者的子网掩码均为 255.255.255.0。 然后,在代码中,我相应地将addr.sin_addr.s_addr = inet_addr("10.0.1.2")用于服务器,addr.sin_addr.s_addr = inet_addr("10.0.1.1")用于客户端。

但是我仍然遇到从一台计算机发送到另一台计算机的消息的问题。

这是代码:

/////////////////////Client Code///////////////////////////////
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
#include <WinSock2.h>
#include <iostream>
int main()
{
//Winsock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0) //If WSAStartup returns anything other than 0, then that means an error has occured in the WinSock Startup.
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
SOCKADDR_IN addr; //Address to be binded to our Connection socket
int sizeofaddr = sizeof(addr); //Need sizeofaddr for the connect function
addr.sin_addr.s_addr = inet_addr("10.0.1.1");
addr.sin_port = htons(139); //Port = 139
addr.sin_family = AF_INET; //IPv4 Socket
SOCKET Connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Set Connection socket
if (connect(Connection, (SOCKADDR*)&addr, sizeofaddr) != 0) //If we are unable to connect...
{
MessageBoxA(NULL, "Failed to Connect", "Error", MB_OK | MB_ICONERROR);
return 0; //Failed to Connect
}
std::cout << "Connected!" << std::endl;
int rec = 0;
char MOTD[256];
while (1)
{
recv(Connection, MOTD, sizeof(MOTD), NULL); //Receive Message of the Day buffer into MOTD array
std::cout << "MOTD:" << MOTD << std::endl;
std::cout << "rec:" << rec << std::endl;
rec++;
Sleep(500);
}
}

/////////////////////Server Code///////////////////////////////
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
#include <WinSock2.h>
#include <iostream>
int main()
{
//WinSock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0) //If WSAStartup returns anything other than 0, then that means an error has occured in the WinSock Startup.
{
MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);
return 0;
}
SOCKADDR_IN addr; //Address that we will bind our listening socket to
int addrlen = sizeof(addr); //length of the address (required for accept call)
addr.sin_addr.s_addr = inet_addr("10.0.1.2");
addr.sin_port = htons(139); //Port
addr.sin_family = AF_INET; //IPv4 Socket
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL); //Create socket to listen for new connections
bind(sListen, (SOCKADDR*)&addr, sizeof(addr)); //Bind the address to the socket
listen(sListen, SOMAXCONN); //Places sListen socket in a state in which it is listening for an incoming connection. Note:SOMAXCONN = Socket Oustanding Max Connections
int counter = 0;
SOCKET newConnection; //Socket to hold the client's connection
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen); //Accept a new connection
if (newConnection == 0) //If accepting the client connection failed
{
std::cout << "Failed to accept the client's connection." << std::endl;
}
else //If client connection properly accepted
{
std::cout << "Client Connected!" << std::endl;
while (counter <100)
{
char MD[256] = "Hi there."; //Create buffer with message 
send(newConnection, MD, sizeof(MD), NULL); //Send MD buffer
counter++;
}
}
system("pause");
return 0;
}

我现在真的不知道该怎么办。 我可以从一台计算机ping到另一台计算机,但是我无法通过以太网连接将消息从一台计算机发送到另一台计算机。

主要问题是客户端连接到错误的 IP。 服务器的 IP 是 10.0.1.2,但客户端正在尝试连接到 10.0.1.1。 这就是为什么它不能在多台计算机上工作的原因。 客户端需要连接到服务器的 IP,而不是客户端的 IP。

此外,您通常会犯其他几个错误。

在服务器端,您忽略了bind()listen()的返回值,并且accept()错误时返回INVALID_SOCKET(-1) 而不是 0。

在客户端,您忽略了recv()的返回值。 出错时返回 -1,正常断开连接时返回 0,实际读取的字节数返回> 0。 您需要注意这一点,尤其是当您将读取的数据发送到std::cout时。 您正在向operator<<传递char[],因此数据必须以 null 结尾,但recv()不能保证这一点。 因此,要么:

  • 读取数据后,在char[]数据的末尾添加一个 null 终止符:

    int numRead = recv(Connection, MOTD, sizeof(MOTD)-1, NULL);
    if (numRead <= 0) break;
    MOTD[numRead] = 0; // <-- here
    std::cout << "MOTD:" << MOTD << std::endl;
    
  • char[]传递给std::cin.write()而不是operator<<,指定在count参数中读取的实际字节数:

    int numRead = recv(Connection, MOTD, sizeof(MOTD), NULL);
    if (numRead <= 0) break;
    std::cout << "MOTD:";
    std::cout.write(MOTD, numRead); // <-- here
    std::cout << std::endl;
    

而且您的 MOTD 协议通常设计得不是很好。 服务器为每条消息发送 256 字节(如果幸运的话,send()可以发送更少的字节!),即使实际只使用了 9 个字节。 所以你在浪费带宽。 客户端期望每次都接收 256 个字节(这不能保证,因为recv()可能会接收更少的字节! 更好的设计是让服务器发送末尾具有终止分隔符(如换行符或空终止符)的字符串,然后让客户端循环读取,直到它收到该分隔符,然后处理已接收的数据。

尝试更多类似的东西:

/////////////////////Client Code///////////////////////////////
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
//Winsock Startup
WSAData wsaData;
int iResult = WSAStartup(MAKEWORD(2, 1), &wsaData);
if (iResult != 0) //If WSAStartup returns anything other than 0, then that means an error has occured in the WinSock Startup.
{
std::cout << "Winsock Startup Failed, Error " << iResult << std:endl;
return 1;
}
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET; //IPv4 Socket
addr.sin_addr.s_addr = inet_addr("10.0.1.2"); //Address to be connected to
addr.sin_port = htons(139); //Port = 139
SOCKET Connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket to establish new connection with
if (Connection == INVALID_SOCKET)
{
iResult = WSAGetLastError();
std::cout << "Failed to Create Socket, Error " << iResult << std::endl;
WSACleanup();
return 1; //Failed to Connect
}
if (connect(Connection, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) //If we are unable to connect...
{
iResult = WSAGetLastError();
std::cout << "Failed to Connect, Error " << iResult << std::endl;
closesocket(Connection);
WSACleanup();
return 1; //Failed to Connect
}
std::cout << "Connected!" << std::endl;
int rec = 0;
char buf[256], *ptr, *start, *end;
int numRead;
std::string MOTD;
int iExitCode = 0;
while (true)
{
numRead = recv(Connection, buf, sizeof(buf), NULL); //Receive data
if (numRead == SOCKET_ERROR)
{
iResult = WSAGetLastError();
std::cout << "Failed to Read, Error " << iResult << std:endl;
iExitCode = 1;
break;
}
if (numRead == 0)
{
std::cout << "Server disconnected!" << std::endl;
break;
}
start = buf;
end = buf + numRead;
do
{
// look for MOTD terminator
ptr = std::find(start, end, '');
if (ptr == end)
{
// not found, need to read more...
MOTD.append(start, end-start);
break;
}
// terminator found, display current MOTD and reset for next MOTD...
MOTD.append(start, ptr-start);
std::cout << "MOTD:" << MOTD << std::endl;
std::cout << "rec:" << rec << std::endl;
rec++;
MOTD = "";
start = ptr + 1;
}
while (start < end);
}
closesocket(Connection);
WSACleanup();
return iExitCode;
}

/////////////////////Server Code///////////////////////////////
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
bool sendAll(SOCKET s, const void *buf, int size)
{
const char *ptr = (const char*) buf;
while (size > 0)
{
int numSent = send(s, ptr, size, NULL);
if (numSent == SOCKET_ERROR) return false;
ptr += numSent;
size -= numSent;
}
return true;
}
int main()
{
//WinSock Startup
WSAData wsaData;
int iResult = WSAStartup(MAKEWORD(2, 1), &wsaData);
if (iResult != 0) //If WSAStartup returns anything other than 0, then that means an error has occured in the WinSock Startup.
{
std::cout << "WinSock Startup Failed, Error " << iResult << std::endl;
return 1;
}
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET; //IPv4 Socket
addr.sin_addr.s_addr = INADDR_ANY; //Address that we will bind our listening socket to. INADDR_ANY = all local IPv4 addresses
addr.sin_port = htons(139); //Port
SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket to listen for new connections
if (sListen == INVALID_SOCKET)
{
iResult = WSAGetLastError();
std::cout << "Failed to Create Socket, Error " << iResult << std::endl;
closesocket(sListen);
WSACleanup();
return 1;
}
if (bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) //Bind the address to the socket
{
iResult = WSAGetLastError();
std::cout << "Failed to Bind Socket, Error " << iResult << std::endl;
closesocket(sListen);
WSACleanup();
return 1;
}
if (listen(sListen, SOMAXCONN) == SOCKET_ERROR) //Places sListen socket in a state in which it is listening for an incoming connection. Note:SOMAXCONN = Socket Outstanding Max Connections
{
iResult = WSAGetLastError();
std::cout << "Failed to Listen, Error " << iResult << std::endl;
closesocket(sListen);
WSACleanup();
return 1;
}
SOCKET newConnection; //Socket to hold the client's connection
int iExitCode = 0;
do
{
std::cout << "Waiting for Client to Connect..." << std::endl;
int addrlen = sizeof(addr); //length of the address (required for accept call)
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen); //Accept a new connection
if (newConnection == INVALID_SOCKET) //If accepting the client connection failed
{
iResult = WSAGetLastError();
std::cout << "Failed to accept a client's connection, Error " << iResult << std::endl;
iExitCode = 1;
break;
}
std::cout << "Client Connected!" << std::endl;
for (int counter = 0; counter < 100; ++counter)
{
std::string MOTD = "Hi there."; //Create buffer with message 
if (!sendAll(newConnection, MOTD.c_str(), MOTD.length()+1))
{
iResult = WSAGetLastError();
std::cout << "Failed to Send, Error " << iResult << std::endl;
break;
}
}
closesocket(newConnection);
std::cout << "Client Disconnected!" << std::endl;
}
while (true);
closesocket(sListen);
WSACleanup();
return iExitCode;
}

感谢您的所有答案和评论!我通过更改端口号解决了这个问题。显然,一些端口号是保留的,所以我必须分配另一个端口号。