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