套接字和 telnet 有问题

Having issues with sockets and telnet

我一直在学习套接字,我已经创建了一个基本的服务器,您可以在其中远程登录并输入消息,然后按回车键,消息就会打印在服务器上。

由于是 telnet,每次按键都会发送到服务器。所以我基本上将所有发送的字节保存在一个缓冲区中,然后当收到回车符 return ("\r\n") 时,我将其丢弃,并打印出客户端当前缓冲区。然后我清除客户端缓冲区。

我的问题是,每隔一段时间(我不太确定如何复制它),我发送的第一个 "line" 数据会附加一个额外的 space每个角色。例如,我将在 telnet 客户端上键入 "Test",但我的服务器将收到它作为 "T e s t "。我总是在接收任何数据之前清除接收缓冲区。一个明显的解决方案就是删除所有 space 的服务器端,但这会扰乱我发送多个单词的能力。这只是我的 telnet 的问题,还是我可以在服务器上做些什么来解决这个问题?

我正在使用 WinSock2 API 和 Windows 10 Telnet。

编辑: 我检查了多余字符的十六进制值,是0x20。

编辑: 下面是接收和处理传入的 telnet 数据的代码。

// This client is trying to send some data to us
                memset(receiveBuffer, sizeof(receiveBuffer), 0);
                int receivedBytes = recv(client->socket, receiveBuffer, sizeof(receiveBuffer), 0);
                if (receivedBytes == SOCKET_ERROR)
                {
                    FD_CLR(client->socket, &masterFDSet);
                    std::cerr << "Error! recv(): " << WSAGetLastError() << std::endl;
                    closesocket(client->socket);
                    client->isDisconnected = true;

                    continue;
                }
                else if (receivedBytes == 0)
                {
                    FD_CLR(client->socket, &masterFDSet);
                    std::cout << "Socket " << client->socket << " was closed by the client." << std::endl;
                    closesocket(client->socket);
                    client->isDisconnected = true;

                    continue;
                }

                // Print out the hex value of the incoming data, for debug purposes
                const int siz_ar = strlen(receiveBuffer);
                for (int i = 0; i < siz_ar; i++)
                {
                    std::cout << std::hex << (int)receiveBuffer[i] << " " << std::dec;
                }
                std::cout << std::endl;

                std::string stringCRLF = "\r\n"; // Carraige return representation
                std::string stringBS = "\b"; // Backspace representation
                std::string commandBuffer = receiveBuffer;
                if (commandBuffer.find(stringCRLF) != std::string::npos)
                {
                    // New line detected. Process message.
                    ProcessClientMessage(client);
                }
                else if (commandBuffer.find(stringBS) != std::string::npos)
                {
                    // Backspace detected,
                    int size = strlen(client->dataBuffer);
                    client->dataBuffer[size - 1] = '[=10=]';
                }
                else
                {
                    // Strip any extra dumb characters that might have found their way in there
                    commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\r'), commandBuffer.end());
                    commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\n'), commandBuffer.end());

                    // Add the new data to the clients data buffer
                    strcat_s(client->dataBuffer, sizeof(client->dataBuffer), commandBuffer.c_str());
                }

                std::cout << "length of data buffer is " << strlen(client->dataBuffer) << std::endl;

你有两个主要问题。

首先,您有一个变量,receivedBytes,它知道您收到的字节数。那你为什么叫strlen?您无法保证收到的数据是 C 风格的字符串。例如,它可以包含嵌入的零字节。不要在上面调用 strlen

其次,您检查刚收到的数据是否 \r\n,而不是完整的接收缓冲区。并且您将数据接收到接收缓冲区的开头,而不是其中第一个未使用的 space 。因此,如果对 recv 的一次调用获得 \r 而下一次调用获得 \n,您的代码将执行错误操作。

您实际上从未编写代码来接收消息。您实际上从未创建 message buffer 来保存收到的消息。

你的代码,我的评论:

memset(receiveBuffer, sizeof(receiveBuffer), 0);

你不需要这个。你不应该需要这个。如果你这样做了,那么你的代码中就有一个错误。

int receivedBytes = recv(client->socket, receiveBuffer, sizeof(receiveBuffer), 0);
if (receivedBytes == SOCKET_ERROR)
{
    FD_CLR(client->socket, &masterFDSet);
    std::cerr << "Error! recv(): " << WSAGetLastError() << std::endl;
    closesocket(client->socket);
    client->isDisconnected = true;

    continue;

你是说 'break'。你有一个错误。你关闭了插座。没有什么可以继续了。

}
else if (receivedBytes == 0)
{
    FD_CLR(client->socket, &masterFDSet);
    std::cout << "Socket " << client->socket << " was closed by the client." << std::endl;
    closesocket(client->socket);
    client->isDisconnected = true;

    continue;

同上。你是说 'break'。你有一个错误。你关闭了插座。没有什么可以继续了。

}

// Print out the hex value of the incoming data, for debug purposes
const int siz_ar = strlen(receiveBuffer);

Bzzzzzzzzzzzz。无法保证缓冲区中的任何位置都为 null。你不需要这个变量。 receivedBytes.

中已经存在正确的值
for (int i = 0; i < siz_ar; i++)

那应该是`for (int i = 0; i < receivedBytes; i++)

{
    std::cout << std::hex << (int)receiveBuffer[i] << " " << std::dec;
}
std::cout << std::endl;

std::string stringCRLF = "\r\n"; // Carraige return representation

没有。那是一个回车符 return (\r),后跟一个换行符 (\n),通常称为 CRLF,因为您确实在变量名中有自己。这是 Telnet 中的标准行终止符。

std::string stringBS = "\b"; // Backspace representation
std::string commandBuffer = receiveBuffer;

嗡嗡声。此副本的长度应由 receivedBytes.

分隔
if (commandBuffer.find(stringCRLF) != std::string::npos)

如@DavidShwartz 所述,您不能假设您在同一缓冲区中获得了 CR 和 LF。

{
    // New line detected. Process message.
    ProcessClientMessage(client);
}
else if (commandBuffer.find(stringBS) != std::string::npos)
{
    // Backspace detected,
    int size = strlen(client->dataBuffer);
    client->dataBuffer[size - 1] = '[=18=]';

这没有任何意义。您正在使用 strlen() 来告诉您尾随的 null 在哪里,然后您将 null 放在那里。您还遇到了可能 尾随空值的问题。在任何情况下,您应该做的是删除退格键和它之前的字符,这需要不同的代码。您还在错误的数据缓冲区上操作。

}
else
{
    // Strip any extra dumb characters that might have found their way in there
    commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\r'), commandBuffer.end());
    commandBuffer.erase(std::remove(commandBuffer.begin(), commandBuffer.end(), '\n'), commandBuffer.end());

    // Add the new data to the clients data buffer
    strcat_s(client->dataBuffer, sizeof(client->dataBuffer), commandBuffer.c_str());
}