如何从标头确定有效负载大小?

Posted

技术标签:

【中文标题】如何从标头确定有效负载大小?【英文标题】:How to determine payload size from header? 【发布时间】:2020-09-16 12:57:40 【问题描述】:

我想从 A 侧发送具有正文大小的正文和标头。 在 B 面,在第一个 recv() 标头中确定有效负载的大小。第二个,recv() 主体。

我尝试使用 recv() 两次,但无法通过第二个 recv() 函数。

A面

struct CommandHeader_t 
    int BodySizeByte;
;

void Send()

    const int BODY_SIZE_BYTE = 10;
    CommandHeader_t* header_ptr = (CommandHeader_t*) malloc(sizeof(CommandHeader_t) + BODY_SIZE_BYTE);
    header_ptr->BodySizeByte = BODY_SIZE_BYTE;

    char* body_ptr = (char*) (header_ptr + sizeof(CommandHeader_t));
    snprintf(body_ptr, BODY_SIZE_BYTE, "Hello World");

    int sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
    struct sockaddr_un addr = 0;
    addr.sun_family = AF_LOCAL;
    strcpy(addr.sun_path, SENDING_SOCKET);
    sendto(sock, header_ptr, sizeof(CommandHeader_t) + BODY_SIZE_BYTE, 0, (const struct sockaddr*)&addr, sizeof(addr));
    close(sock);

B面

void Reveice()

    // something
    CommandHeader_t header;
    char* body_ptr;
    recv(sockfd, &header, sizeof(CommandHeader_t), 0);
    body_ptr = (char*) malloc(header.BodySizeByte);
    recv(sockfd, body_ptr, header.BodySizeByte, 0); // CANNOT recv anything

【问题讨论】:

看起来您发送了一个带有标题和内容的数据报。接收方收到了数据报,但是recv()只表示了报头的大小。因此,数据报的其余部分被丢弃并丢弃。因此,在第二次接收中,没有汤适合你。 建议:不要忽略返回码。它们几乎总是包含重要信息,例如“我真正得到了多少字节的数据?”或“我得到什么了吗?”以及曾经流行的“嘿,我的插座还活着吗?”。 由于这是 UDP,您甚至没有提及,因此无需发送有效负载大小。 recv() 的返回值已经提供了。 【参考方案1】:

你的代码有一些错误:

在发送端,分配body_ptr 指针时,您没有正确执行指针运算。因为header_ptr 被声明为CommandHeader_t*,所以执行(header_ptr + sizeof(CommandHeader_t)) 将计算一个内存地址,该地址是sizeof(CommandHeader_t) 数量的CommandHeader_t 元素超过header_ptr 指向的地址。那不是你想要的。相反,您需要计算一个地址,该地址是 sizeof(CommandHeader_t)chars 数量超过 header_ptr 地址。

所以,你需要改变这个:

char* body_ptr = (char*) (header_ptr + sizeof(CommandHeader_t));

改为:

char* body_ptr = ((char*) header_ptr) + sizeof(CommandHeader_t);

更简单的处理方法是在CommandHeader_t 中添加一个char[1] 成员,然后在malloc()'ing 时减1,例如:

#pragma pack(push, 1) // or equivalent
struct CommandHeader_t 
    int BodySizeByte;
    char data[1];
;
#pragma pack(pop) // or equivalent

CommandHeader_t* header_ptr = (CommandHeader_t*) malloc(offsetof(CommandHeader_t, data) + BODY_SIZE_BYTE);
header_ptr->BodySizeByte = BODY_SIZE_BYTE;

char* body_ptr = header_ptr->data;
snprintf(body_ptr, BODY_SIZE_BYTE, "Hello World");

另外,由于您在发送端使用SOCK_DGRAMsendto() 是面向消息的,在 1 条消息中发送整个数据。但是您的接收代码是编写的,就好像它使用的是面向流的套接字一样。不要像这样不匹配套接字类型。如果要多次调用recv(),请在两侧使用面向流的套接字。否则,在两边都使用面向消息的套接字,然后让接收者分配一个足够大的缓冲区以接收 1 recvfrom() 中的整个消息(您可以使用ioctl(FIONREAD) 来确定下一条待处理消息的大小),然后根据需要解析该消息的内容。

此外,您的接收器正在泄漏它使用malloc() 分配的body_ptr 缓冲区。

【讨论】:

以上是关于如何从标头确定有效负载大小?的主要内容,如果未能解决你的问题,请参考以下文章

UDP数据包字节读取粒度?

如何限制特定 API 网关路由的有效负载大小

可有效存储在 tar 存档标头的大小字段中的最大无符号整数是多少

如何在scrapy中发送带有标头和有效负载的Post请求

如果我在 C# 中发送 0 有效载荷数据,udp 数据包的大小是多少?

什么是开销、有效负载和标头 [关闭]