【问题标题】:Having issues with sockets and telnet套接字和 telnet 有问题
【发布时间】:2016-08-26 15:18:18
【问题描述】:

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

由于是远程登录,每次按键都会发送到服务器。所以我基本上将所有发送的字节保存在一个缓冲区中,然后当收到回车符 ("\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;

【问题讨论】:

  • 您的额外空间是真实空间吗?或任何其他不可打印的字符?
  • 您应该为第二个问题打开一个新问题,因为您的两个问题根本没有关联。无论如何,处理任何平台的解决方案是等待\r\n\r\n
  • @kadriles ,我检查了传入数据的十六进制值,多余的字符是0x20,所以它是一个实际的空间。另外,我将删除第二个问题/移动它。
  • 请出示您的代码。否则就没有办法(纯粹的推测除外)来诊断问题。
  • \r\n 是回车后跟换行符,而不仅仅是回车。接收缓冲区的长度由receivedBytes 给出,而不是由strlen() 给出。在 strlen() 已经找到的地方放置一个空值没有任何意义。

标签: c++ sockets winsock telnet


【解决方案1】:

你有两个主要问题。

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

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

您实际上从未编写过代码来接收消息。你从来没有真正创建一个 message buffer 来保存收到的消息。

【讨论】:

  • 糟糕,我没有意识到我可以只使用receivedBytes
  • 你不能just使用 receivedBytes,因为接收到的数据不会被 nul 终止,这取决于你。
  • 至于“在缓冲区的开头接收数据”,这是应该发生的。每次 telnet 发送一个字节时,它都会被写入该缓冲区并覆盖那里的任何内容。尽管您是对的,但我应该检查完整缓冲区中的“\r\n”(将所有字符添加到其中的缓冲区。)
  • @Dooskington 你需要让“commandBuffer”成为来自客户端的现有数据->dataBuffer 加上你刚刚收到的数据,直到你得到一个\r\n,然后您可能想检查您收到两个 \r\ns 并逐行工作的情况。
  • @Dooskington 当您有大量错误时,您不能总是选择先修复哪个错误。编写一个理智的“接收消息”功能。然后尝试找出您可能遇到的其他问题,
【解决方案2】:

你的代码,我的 cmets:

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

Bzzzzzzzzzzzzt。不能保证缓冲区中的任何地方都存在空值。你不需要这个变量。 receivedBytes 中已经存在正确的值。

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

那应该是`for (int i = 0; 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());
}

【讨论】:

  • 您好,您能解释一下“此副本的长度应由 receivedBytes 分隔。”是什么意思吗?
  • 当您将缓冲区复制到字符串中时,您应该使用任何构造函数来定义从缓冲区获取的字节数到字符串中,而不是复制整个缓冲区,或者最多为空,或者无论当前代码做什么。我不记得字符串构造函数了。
  • 我能弄明白。谢谢! client-&gt;inputBuffer.append(receiveBuffer, receivedBytes);
猜你喜欢
  • 1970-01-01
  • 2018-03-26
  • 2012-04-07
  • 2012-02-27
  • 1970-01-01
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
相关资源
最近更新 更多