如何在 TCP/IP 中为消息长度添加前缀

发布于 2024-08-03 06:17:22 字数 1778 浏览 5 评论 0原文

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

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

我正在使用 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;
}

I'm sending messages over TCP/IP, I need to prefix message length in a char array and then send it. How do I do it?

Also can you please provide an example of how to extract it at the another end. And if possible, please explain.

I'm using C++ and Winsock.

EDIT:

string writeBuffer = "Hello";

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

It's not returning the correct length rather a very large number.

For the receiving part, if I use ntohl(), then I also get a large number instead of the correct length? Why is that so? I'm receiving like this

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

双马尾 2024-08-10 06:17:22

在 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;
}

When sending a length field on a TCP stream, you need to decide two things:

  1. what length should the length have (1 byte, 2 bytes, 4 bytes, variable length)
  2. what endianness should I use

I recommend to use 4 bytes length, and network byte order (i.e. big-endian). For network byte order, the macros htonl and ntohl will convert between host (native) byte order (little-endian, in your case), and network byte order.

To send data, the fragment should look like this:

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

On the receiving side, you extract first the length, then the data:

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

What this code is missing is error handling: each of the send and receive calls may fail; the recvs may receive less data than expected. But this should give you an idea.

Edit: To deal with the case that the recv returns too few data, run it in a loop, keeping a count of what you have read so far, e.g.

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;
}
聽兲甴掵 2024-08-10 06:17:22

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

发送者:

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

Pseudocode (error handling omitted, be careful):

sender:

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

receiver:

u_long messageLength;
recv( socket, (char*)&messageLength, sizeof( messageLength ), 0 );
messageLength = ntohl( messageLength ); convert from network byte order to local byte order
卖梦商人 2024-08-10 06:17:22

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

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

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

I'm not sure if this is what you are asking, but I would send it as a string of a fixed length (let's say 4 characters, actual length up to you). In other words, if the data length is 164, send the string "0164". This gets over any problems of byte order at the other end, and is easy to read and debug.

To produce such strings, you can use a stringstream:

#include <sstream>
#include <iomanip>

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

To send:

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

To read it at the other end, something like:

char a[5] = {0};
recv( sock, a, 4 );
int n = atoi( a );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文