如何根据 header 确定负载大小?

How to determine payload size from header?

我想从 A 面发送 () body 和 header,尺寸为 body。 在 B 面,在第一个 recv() header 中确定 payload 的大小。第二个,recv() body.

我尝试使用 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
}

你的代码有一些错误:

在发送方,您在分配 body_ptr 指针时没有正确地进行指针运算。由于 header_ptr 被声明为 CommandHeader_t*,执行 (header_ptr + sizeof(CommandHeader_t)) 将计算一个内存地址,该地址是 sizeof(CommandHeader_t) 个超过 header_ptr 地址的 CommandHeader_t 个元素指着。那不是你想要的。您需要改为计算一个地址,该地址是 header_ptr 地址后 sizeof(CommandHeader_t)char 的地址。

所以,你需要改变这个:

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

改为:

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

一个更简单的处理方法是在 CommandHeader_t 中添加一个 char[1] 成员,然后在 malloc() 时减去 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_DGRAM,因此 sendto() 是面向消息的,在 1 条消息中发送全部数据。但是您的接收代码的编写方式就好像它是在使用面向流的套接字一样。不要像那样不匹配套接字类型。如果要多次调用recv(),两边都使用面向流的socket。否则,双方都使用面向消息的套接字,然后让接收方分配一个足够大的缓冲区,以在 1 recvfrom() 中接收整个消息(您可以使用 ioctl(FIONREAD) 来确定下一个的大小待处理消息),然后根据需要解析该消息的内容。

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