Как определить размер полезной нагрузки из заголовка? - PullRequest
0 голосов
/ 29 мая 2020

Я хочу отправить тело и заголовок send (), которые имеют размер тела со стороны 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
}

1 Ответ

1 голос
/ 29 мая 2020

В вашем коде есть некоторые ошибки:

На передающей стороне вы неправильно выполняете арифметику указателя c при назначении указателя body_ptr. Поскольку header_ptr объявлен как CommandHeader_t*, выполнение (header_ptr + sizeof(CommandHeader_t)) будет вычислять адрес памяти, который равен sizeof(CommandHeader_t) количеству CommandHeader_t элементов после адреса, на который указывает header_ptr. Это не то, что вам нужно. Вместо этого вам нужно вычислить адрес, который равен sizeof(CommandHeader_t) количеству char s после адреса header_ptr.

Итак, вам нужно изменить это:

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

На это вместо этого:

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

Более простой способ справиться с этим - добавить член char[1] к CommandHeader_t, а затем вычесть 1, когда он malloc(), например:

#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() несколько раз, используйте поточно-ориентированный сокет с обеих сторон. В противном случае используйте сокет, ориентированный на сообщения, с обеих сторон, а затем попросите получателя выделить буфер, достаточно большой для приема всего сообщения размером 1 recvfrom() (вы можете использовать ioctl(FIONREAD), чтобы определить размер следующего ожидающего сообщения), а затем проанализируйте содержимое этого сообщения по мере необходимости.

Кроме того, ваш получатель пропускает буфер body_ptr, выделенный им с помощью malloc().

...