套接字和telnet出现问题

Having issues with sockets and telnet

本文关键字:问题 telnet 套接字      更新时间:2023-10-16

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

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

我的问题是,每隔一段时间(我不太确定如何复制它),我发送的第一行数据就会在每个字符上增加一个额外的空间。例如,我将在telnet客户端上键入"Test",但我的服务器将以"T e s T"的形式接收它。我总是在接收任何数据之前清空接收缓冲区。一个显而易见的解决方案是删除服务器端的所有空格,但这会打乱我发送多个单词的能力。这只是我的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 = "rn"; // 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] = '';
                }
                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

其次,检查刚刚收到的rn数据,而不是完整的接收缓冲区。并且您将数据接收到接收缓冲区的开头,而不是其中第一个未使用的空间。因此,如果一个对recv的调用得到r,下一个得到n,那么您的代码将做错误的事情。

您从未真正编写代码来接收消息。您从未实际创建消息缓冲区来保存接收到的消息。

您的代码,我的评论:

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);

Bzzzzzzzzt。不能保证缓冲区中的任何位置都有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 = "rn"; // Carraige return representation

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

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

Bzzt。此副本的长度应由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] = '';

这没有任何意义。您使用strlen()来告诉后面的null在哪里,然后在那里放一个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());
}