我一直在学习套接字,我已经创建了一个基本服务器,您可以在其中远程登录并输入消息,然后按回车键,消息将打印在服务器上。
由于它是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;
答案 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());
}