简单的客户端/服务器 TCP c++
Simple client / server TCP c++
我一直都能找到一种方法来修复我的代码,只是调试和浏览网页,但我现在被困在一些代码上,我不知道我可以做什么样的测试来调试它。
基本上我试图在 C++ 中实现一个简单的 Client/Server 关系,我对每一行都进行了注释以确保我理解我在做什么,但它仍然不起作用。
这是我的客户端代码:
void startClient(){
int wsaStatus, connectStatus; //check errors
WSADATA WSAData;
wsaStatus=WSAStartup(MAKEWORD(2, 0), &WSAData);
if (wsaStatus != NO_ERROR) {
std::cout << "WSA Startup failed with error : " << wsaStatus;
}
SOCKET sock; //defines the sockets TO SEND
SOCKADDR_IN sin;//information about the socket
sin.sin_addr.s_addr = inet_addr("127.0.0.1");//ip of the server you want to connect to
sin.sin_family = AF_INET;//family of the socket, for internet it's AF_INET
sin.sin_port = htons(1234);// 23 for telnet etc, it's the port
sock = socket(AF_INET, SOCK_STREAM, 0);//second parameter is the type of the socket, SOCK_STREAM opens a connection ( use for TCD ), SOCK_DGRAM doesn't connect() or accept() it's used for UDP
if (sock == INVALID_SOCKET) {
std::cout << "INVALID SOCKET " << WSAGetLastError();
WSACleanup();
}
bind(sock, (SOCKADDR *)&sin, sizeof(sin)); //binds the socket to the port and the adress above
char buffer[255]; //creates a buffer to receive messages
connectStatus=connect(sock, (SOCKADDR *)&sin, sizeof(sin)); //function to connect to the server
if (connectStatus == SOCKET_ERROR) { //it returns 0 if no error occurs
std::cout << "Connection failed with error : " << WSAGetLastError();
closesocket(sock);
WSACleanup();
}
int iResult = send(sock, "Hello world!\r\n", 14, 0);
if (iResult == SOCKET_ERROR) {
std::cout << "Send failed with error : " << WSAGetLastError() << std::endl;
}
closesocket(sock);
WSACleanup();
system("pause");
这是我的服务器代码:
void startServer(){
int wsaStatus; //check errors
WSADATA WSAData;
wsaStatus=WSAStartup(MAKEWORD(2, 0), &WSAData);
if (wsaStatus != NO_ERROR) {
std::cout << "WSA Startup failed with error : " << wsaStatus;
}
SOCKET sock; //defines the sockets
SOCKADDR_IN sin; //information about the socket
sin.sin_addr.s_addr = htonl(INADDR_ANY); //since it's the server we accept any connection
sin.sin_family = AF_INET; //family of the socket, for internet it's AF_INET
sin.sin_port = htons(1234); // 23 for telnet etc, it's the port
sock = socket(AF_INET, SOCK_STREAM, 0); //second parameter is the type of the socket, SOCK_STREAM opens a connection ( use for TCD ), SOCK_DGRAM doesn't connect() or accept() it's used for UDP
if (sock == INVALID_SOCKET) {
std::cout << "INVALID SOCKET " << WSAGetLastError();
WSACleanup();
}
bind(sock, (SOCKADDR *)&sin, sizeof(sin)); //binds the socket to the port and the adress above
char buffer[255]; //to receive the messages
listen(sock, 1); //listens on the port of the socket, second parameter is the maximum of connections accepted
while (1)
{
int sizeof_sin = sizeof(sin); //size of the socket used to take the information from the client connected
sock = accept(sock, (SOCKADDR *)&sin, &sizeof_sin); //first parameter : socket, second parameter : client information socket, third parameter : size of the information about the socket
std::cout << "Connection ok" << std::endl;
if (sock != INVALID_SOCKET)
{
recv(sock, buffer, sizeof(buffer), 0);
closesocket(sock);
std::cout << buffer << std::endl;
}
else{
std::cout << "ERROR" << std::endl;
}
}
WSACleanup();
所以我首先启动服务器,它在等待连接的 accept() 函数处停止,然后我启动成功发送消息的客户端,但服务器仍在等待 accept() 函数,而不是收到任何消息。
如您所见,我实施了很多错误检查,但仍然无济于事:')
干杯!
为什么要在客户端代码中调用 bind?这是没有必要的。绑定只能由服务器调用。 TCP 客户端将使用随机端口与服务器通信,因此无需在客户端进行绑定。此外,来自两个不同进程的同一端口上的绑定将失败,因为该端口已被使用。因此,删除绑定并重新测试您的代码。
为了单独测试,可以使用netcat工具。这是一个非常简单的工具,可以充当服务器或客户端:
https://es.wikipedia.org/wiki/Netcat
所以要测试客户端,只需在服务器模式下启动 netcat,如下所示:
nc -l your_ip port
然后启动您的客户端,因此您应该会看到客户端正确连接到已启动的服务器。同样,您可以通过在客户端模式下启动 nc 来测试您的服务器。
此外,启动服务器后,您可以使用 netstat(作为 root)工具来确保服务器 运行 在正确的 IP/port.因此,像下面这样的命令可能有助于查看服务器的 TCP 套接字处于哪种状态(侦听等)。看看 netstat 的手册页,它是另一个非常有用的工具。
netstat -anp | grep your_port
使用 netstat 您还可以看到客户端在连接到您的服务器后使用的端口以及 TCP 连接的状态。
您正在尝试将客户端和服务器绑定到同一个端口,并且您没有检查任何一种情况下绑定的 return 值是否有错误。绑定会经常失败,因为另一个程序可能正在使用您想要的端口,因此您应该始终检查它的 return 值。
一般情况下,客户端不需要绑定,除非你真的想让它有一个特定的本地端口,而且在大多数情况下,你不关心它使用的是什么端口。服务器必须绑定,因为您确实需要知道端口才能连接。您不需要(通常)知道任何客户端端口。
此外,在服务器中,调用 accept 时不应重复使用 sock
变量。为 accept 的 return 值创建一个新的套接字变量。现在,您的程序正在泄漏资源,因为您的侦听套接字保持打开状态,但您无法访问它,因为您覆盖了 sock 变量。
由于您使用的是 Windows,因此 TCPView from SysInternals 是帮助套接字程序的一个很好的工具。它列出了系统上所有打开的套接字及其当前状态和传输的字节数。
我一直都能找到一种方法来修复我的代码,只是调试和浏览网页,但我现在被困在一些代码上,我不知道我可以做什么样的测试来调试它。
基本上我试图在 C++ 中实现一个简单的 Client/Server 关系,我对每一行都进行了注释以确保我理解我在做什么,但它仍然不起作用。
这是我的客户端代码:
void startClient(){
int wsaStatus, connectStatus; //check errors
WSADATA WSAData;
wsaStatus=WSAStartup(MAKEWORD(2, 0), &WSAData);
if (wsaStatus != NO_ERROR) {
std::cout << "WSA Startup failed with error : " << wsaStatus;
}
SOCKET sock; //defines the sockets TO SEND
SOCKADDR_IN sin;//information about the socket
sin.sin_addr.s_addr = inet_addr("127.0.0.1");//ip of the server you want to connect to
sin.sin_family = AF_INET;//family of the socket, for internet it's AF_INET
sin.sin_port = htons(1234);// 23 for telnet etc, it's the port
sock = socket(AF_INET, SOCK_STREAM, 0);//second parameter is the type of the socket, SOCK_STREAM opens a connection ( use for TCD ), SOCK_DGRAM doesn't connect() or accept() it's used for UDP
if (sock == INVALID_SOCKET) {
std::cout << "INVALID SOCKET " << WSAGetLastError();
WSACleanup();
}
bind(sock, (SOCKADDR *)&sin, sizeof(sin)); //binds the socket to the port and the adress above
char buffer[255]; //creates a buffer to receive messages
connectStatus=connect(sock, (SOCKADDR *)&sin, sizeof(sin)); //function to connect to the server
if (connectStatus == SOCKET_ERROR) { //it returns 0 if no error occurs
std::cout << "Connection failed with error : " << WSAGetLastError();
closesocket(sock);
WSACleanup();
}
int iResult = send(sock, "Hello world!\r\n", 14, 0);
if (iResult == SOCKET_ERROR) {
std::cout << "Send failed with error : " << WSAGetLastError() << std::endl;
}
closesocket(sock);
WSACleanup();
system("pause");
这是我的服务器代码:
void startServer(){
int wsaStatus; //check errors
WSADATA WSAData;
wsaStatus=WSAStartup(MAKEWORD(2, 0), &WSAData);
if (wsaStatus != NO_ERROR) {
std::cout << "WSA Startup failed with error : " << wsaStatus;
}
SOCKET sock; //defines the sockets
SOCKADDR_IN sin; //information about the socket
sin.sin_addr.s_addr = htonl(INADDR_ANY); //since it's the server we accept any connection
sin.sin_family = AF_INET; //family of the socket, for internet it's AF_INET
sin.sin_port = htons(1234); // 23 for telnet etc, it's the port
sock = socket(AF_INET, SOCK_STREAM, 0); //second parameter is the type of the socket, SOCK_STREAM opens a connection ( use for TCD ), SOCK_DGRAM doesn't connect() or accept() it's used for UDP
if (sock == INVALID_SOCKET) {
std::cout << "INVALID SOCKET " << WSAGetLastError();
WSACleanup();
}
bind(sock, (SOCKADDR *)&sin, sizeof(sin)); //binds the socket to the port and the adress above
char buffer[255]; //to receive the messages
listen(sock, 1); //listens on the port of the socket, second parameter is the maximum of connections accepted
while (1)
{
int sizeof_sin = sizeof(sin); //size of the socket used to take the information from the client connected
sock = accept(sock, (SOCKADDR *)&sin, &sizeof_sin); //first parameter : socket, second parameter : client information socket, third parameter : size of the information about the socket
std::cout << "Connection ok" << std::endl;
if (sock != INVALID_SOCKET)
{
recv(sock, buffer, sizeof(buffer), 0);
closesocket(sock);
std::cout << buffer << std::endl;
}
else{
std::cout << "ERROR" << std::endl;
}
}
WSACleanup();
所以我首先启动服务器,它在等待连接的 accept() 函数处停止,然后我启动成功发送消息的客户端,但服务器仍在等待 accept() 函数,而不是收到任何消息。
如您所见,我实施了很多错误检查,但仍然无济于事:')
干杯!
为什么要在客户端代码中调用 bind?这是没有必要的。绑定只能由服务器调用。 TCP 客户端将使用随机端口与服务器通信,因此无需在客户端进行绑定。此外,来自两个不同进程的同一端口上的绑定将失败,因为该端口已被使用。因此,删除绑定并重新测试您的代码。
为了单独测试,可以使用netcat工具。这是一个非常简单的工具,可以充当服务器或客户端:
https://es.wikipedia.org/wiki/Netcat
所以要测试客户端,只需在服务器模式下启动 netcat,如下所示:
nc -l your_ip port
然后启动您的客户端,因此您应该会看到客户端正确连接到已启动的服务器。同样,您可以通过在客户端模式下启动 nc 来测试您的服务器。
此外,启动服务器后,您可以使用 netstat(作为 root)工具来确保服务器 运行 在正确的 IP/port.因此,像下面这样的命令可能有助于查看服务器的 TCP 套接字处于哪种状态(侦听等)。看看 netstat 的手册页,它是另一个非常有用的工具。
netstat -anp | grep your_port
使用 netstat 您还可以看到客户端在连接到您的服务器后使用的端口以及 TCP 连接的状态。
您正在尝试将客户端和服务器绑定到同一个端口,并且您没有检查任何一种情况下绑定的 return 值是否有错误。绑定会经常失败,因为另一个程序可能正在使用您想要的端口,因此您应该始终检查它的 return 值。
一般情况下,客户端不需要绑定,除非你真的想让它有一个特定的本地端口,而且在大多数情况下,你不关心它使用的是什么端口。服务器必须绑定,因为您确实需要知道端口才能连接。您不需要(通常)知道任何客户端端口。
此外,在服务器中,调用 accept 时不应重复使用 sock
变量。为 accept 的 return 值创建一个新的套接字变量。现在,您的程序正在泄漏资源,因为您的侦听套接字保持打开状态,但您无法访问它,因为您覆盖了 sock 变量。
由于您使用的是 Windows,因此 TCPView from SysInternals 是帮助套接字程序的一个很好的工具。它列出了系统上所有打开的套接字及其当前状态和传输的字节数。