3

我正在通过 TCP/IP 发送消息,我需要在 char 数组中添加消息长度前缀,然后发送它。我该怎么做?

您还可以提供一个如何在另一端提取它的示例。如果可能,请解释一下。

我正在使用 C++ 和 Winsock。

编辑:

string writeBuffer = "Hello";

unsigned __int32 length = htonl(writeBuffer.length());

它没有返回正确的长度,而是一个非常大的数字。

对于接收部分,如果我使用 ntohl(),那么我也会得到一个大数字而不是正确的长度?为什么呢?我收到这样的

bool Server::Receive(unsigned int socketIndex)
{
    // Read data from the socket
    if (receivingLength)
    {
        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            ((char*)&messageLength) + bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == MESSAGE_LENGTH_SIZE)
        {
            // If uncomment the following line,
            // I won't get the correct length, but a large number
            //messageLength = ntohl(messageLength);
            receivingLength = false;
            bytesReceived = 0;
            bytesLeft = messageLength;
        }
    }
    else
    {
        if (bytesLeft > BUFFER_SIZE)
        {
            return false;
        }

        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            &receiveBuffer[bytesReceived], bytesLeft, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == messageLength)
        {
            // we have received full message
            messageReceived = true;

            receiveBuffer[bytesReceived] = '\0';

            // wait for next message
            receivingLength = true;
        }

        bytesLeft -= bytesReceived;
    }

    return true;
}
4

3 回答 3

7

在 TCP 流上发送长度字段时,您需要确定两件事:

  1. 长度应该是多少(1字节,2字节,4字节,可变长度)
  2. 我应该使用什么字节序

我建议使用 4 字节长度和网络字节顺序(即大端)。对于网络字节顺序,宏 htonl 和 ntohl 将在主机(本机)字节顺序(在您的情况下为小端)和网络字节顺序之间进行转换。

要发送数据,片段应如下所示:

size_t length = strlen(data);
uint32_t nlength = htonl(length);
send(sock, &nlength, 4, 0);
send(sock, data, length, 0);

在接收端,您首先提取长度,然后提取数据:

uint32_t length, nlength;
recv(sock, &nlength, 4, 0);
length = ntohl(nlength);
data = malloc(length+1);
recv(sock, data, length, 0);
data[length] = 0;

这段代码缺少的是错误处理:每个发送和接收调用都可能失败;recvs 接收的数据可能比预期的少。但这应该给你一个想法。

编辑:为了处理recv返回的数据太少的情况,在循环中运行它,记录你到目前为止读过的内容,例如

int length_bytes = 0;
while(length_bytes < 4){
   int read = recv(sock, ((char*)&nLength)+length_bytes, 4-length_bytes, 0);
   if (read == -1) some_error_occurred_check_errno();
   length_bytes += read;
}
于 2009-08-27T07:03:11.953 回答
1

伪代码(错误处理省略,小心):

发件人:

u_long converted = htonl( messageLength ); // convert from local byte order to network byte order
send( socket, (char*)&converted, sizeof( converted ), 0 );

接收者:

u_long messageLength;
recv( socket, (char*)&messageLength, sizeof( messageLength ), 0 );
messageLength = ntohl( messageLength ); convert from network byte order to local byte order
于 2009-08-27T07:01:35.653 回答
0

我不确定这是否是您要问的,但我会将其作为固定长度的字符串发送(比如说 4 个字符,实际长度由您决定)。也就是说,如果数据长度为164,则发送字符串“0164”。这解决了另一端的任何字节顺序问题,并且易于阅读和调试。

要生成这样的字符串,您可以使用 stringstream:

#include <sstream>
#include <iomanip>

std::string MakeLenStr( int n ) {
  std::ostringstream os;
  os << std::setw(4) << std::setfill('0') << n;
  return os.str();
}

发送:

std::string s = MakeLenStr( len );
send( sock, s.c_str(), 4 );

要在另一端阅读它,例如:

char a[5] = {0};
recv( sock, a, 4 );
int n = atoi( a );
于 2009-08-27T07:00:09.150 回答