有套接字和telnet的问题

时间:2016-05-01 22:16:38

标签: c++ sockets winsock telnet

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

由于它是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 = "\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] = '\0';
                }
                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;

2 个答案:

答案 0 :(得分:2)

你有两个主要问题。

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

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

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

答案 1 :(得分:0)

您的代码,我的意见:

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

你不需要这个。你不应该需要这个。如果你这样做,你的代码后面会有一个bug。

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

Bzzzzzzzzzzzzt。无法保证缓冲区中的任何位置都存在空值。你不需要这个变量。 receivedBytes中已存在正确的值。

for (int i = 0; i < siz_ar; i++)

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

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

std::string stringCRLF = "\r\n"; // 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] = '\0';

这没有任何意义。您正在使用strlen()告诉您尾随空值的位置,然后您在那里放置一个空值。您还有一个问题,即 可能没有尾随空值。在任何情况下,你应该做的是删除退格和前面的字符,这需要不同的代码。您还在错误的数据缓冲区上运行。

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