【问题标题】:Simple client / server TCP c++简单的客户端/服务器 TCP c++
【发布时间】:2015-12-29 11:48:09
【问题描述】:

我一直能够找到一种方法来修复我的代码,只是调试和浏览网页,但我现在卡在一些代码上,我看不出我可以做什么样的测试来调试它。

基本上我正在尝试在 C++ 中实现一个简单的客户端/服务器关系,我评论了每一行以确保我理解我在做什么,但它仍然不起作用。

这是我的客户代码:

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(sock, (SOCKADDR *)&sin, sizeof(sin));返回值。是否成功。
  • 如果在 linux 上,你可以打印 strerror(errno) 以获取操作系统通知的错误,但我不知道它在 windows 中是如何完成的
  • 在您的服务器代码中,您正在重用您监听的同一个套接字来存储接受的结果。 Accept 创建一个全新的套接字,所以使用一个新的套接字变量。
  • 另外,在客户端,不需要调用bind。套接字将在连接时隐式绑定 - 现在您正在尝试将客户端和服务器绑定到同一个端口。这不会奏效。
  • 首先,谢谢大家的回答,这真的帮助我理解了。我认为这里的问题是我对套接字如何工作的理解不足! -我将在代码正常工作后立即对其进行编辑(现在客户端/服务器通信正常,但我仍有一些可以修复的错误)

标签: c++ sockets tcp server client


【解决方案1】:

您为什么在客户端代码中调用 bind?这是没有必要的。 Bind 只能由服务器调用。 TCP客户端将使用随机端口与服务器通信,因此不需要在客户端绑定。此外,来自两个不同进程的同一端口上的绑定将失败,因为该端口已在使用中。因此,删除绑定并重新测试您的代码。

为了单独测试,您可以使用 netcat 工具。这是一个非常简单的工具,可以充当服务器或客户端:

https://es.wikipedia.org/wiki/Netcat

所以要测试客户端只需在服务器模式下启动 netcat,如下所示:

nc -l your_ip port

并启动您的客户端,因此您应该会看到客户端正确连接到已启动的服务器。同样,您可以通过在客户端模式下启动 nc 来测试您的服务器。

此外,启动服务器后,您可以使用 netstat(以 root 身份)工具确保服务器在正确的 IP/端口上运行。因此,类似以下的命令可能有助于查看服务器的 TCP 套接字处于哪种状态(侦听等)。看看 netstat 的手册页,这是另一个非常有用的工具。

netstat -anp | grep your_port

使用 netstat,您还可以查看客户端连接到服务器后正在使用的端口以及 TCP 连接的状态。

【讨论】:

    【解决方案2】:

    您正在尝试将客户端和服务器绑定到同一个端口,并且在任何一种情况下您都没有检查 bind 的返回值是否有错误。 Bind 会经常失败,因为另一个程序可能正在使用您想要的端口,因此您应该始终检查它的返回值。

    一般来说,客户端不需要绑定,除非你真的希望它有一个特定的本地端口,而且在大多数情况下,你并不关心它使用什么端口。服务器必须绑定,因为您确实需要知道端口才能连接。您不需要知道(通常)任何东西的客户端端口。

    另外,在服务器中,当您调用accept 时,不应重复使用sock 变量。为accept的返回值创建一个新的socket变量。现在,您的程序正在泄漏资源,因为您的侦听套接字仍然打开,但您无法访问它,因为您覆盖了 sock 变量。

    由于您使用的是 Windows,因此有助于处理套接字程序的好工具是 TCPView from SysInternals。它列出了系统上所有打开的套接字及其当前状态和传输的字节数。

    【讨论】:

    • 感谢您的帮助,我使它工作,我不明白的最后一件事是您说“您不在乎客户端端口正在使用什么”但是如果我将端口更改为另一个端口与服务器端口不同,则它不起作用。我做错什么了吗?
    • 在哪里改?在您的连接调用中,它必须与服务器相同,因为这是它知道如何连接的唯一方式。连接的套接字有一个本地端口和一个远程端口。您传递给连接的将是“远程”端口,而您绑定的将是本地的。如果不绑定,则随机选择一个本地端口。
    猜你喜欢
    • 2011-11-14
    • 2011-11-16
    • 1970-01-01
    • 2013-04-26
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    相关资源
    最近更新 更多