Как оптимизировать размер отправляемого сообщения сокета, не пропуская никаких данных? - PullRequest
0 голосов
/ 08 апреля 2019

У меня такой вопрос ниже структура, которую я должен заполнить и отправить через сокет

struct Mystruct
{
    int numofarray1elements;
    array1[50]; 
    int numofarray2elements;
    array2[25];
};

Здесь размер 1 члена массива1 составляет 1024 байта, т.е. общий размер массива1 = 50 * 1024 = 51200 байтов размер 1 члена массива 2, скажем, 500, поэтому общий размер массива 2 = 12500 байт

всякий раз, когда я использую send api сокета (сокета домена unix), я должен отправить 51200 + 12500 + 4 + 4 = 63708 байт

Проблема в том, что я должен отправить весь размер структуры, даже если у меня очень мало numofarray1elements и numofarray2elements это приводит к проблеме производительности где почти во всех случаях мои исходные данные могут быть менее 10 КБ, но в итоге я отправляю 63 КБ каждый раз

я не могу хранить динамические массивы в качестве сообщения сокета я уже оптимизировал свои данные, массив1 должен содержать не более 50 элементов массив2 должен содержать не более 25 элементов.

теперь есть ли способ отправить точные данные, которые я заполнил?

укажите какой-либо метод, если таковой имеется

Спасибо

Ответы [ 3 ]

1 голос
/ 09 апреля 2019

На самом деле способ сделать это - иметь сообщения переменной длины. Одним из методов является использование одного массива с неопределенным размером в качестве последнего элемента структуры. В зависимости от типов сообщений оно может быть представлено сообщениями или байтами, например,

struct Mystruct
{
    int numofarray1elements;
    int numofarray2elements;
    char array[];
};

Размер вашей структуры может быть рассчитан как размер статических полей плюс размеры, необходимые для фактической полезной нагрузки, как это:

 int packetSize = (sizeof(struct Mystruct) + n1 * sizeof(el1) + n2 * sizeof(el2));

теперь вы можете использовать его для выделения структуры и отправки пакета за одну операцию.

  struct Mystruct *packet = malloc(packetSize);
  // assign packet fields
  ...
  write(fd, packet, packetSize);

Вот простой пример, который эмулирует версию записи / чтения. Это будет работать, если писатель и читатель имеют одинаковый порядок байтов. Также предполагается, что размер пакета отправляется отдельно и известен читателю.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct Mystruct {
    int numofarray1elements;
    int numofarray2elements;
    char payload[];
};

struct Element1 {
    int len;
    char name[30];
};

struct Element2 {
    char name[20];
    int len;
};

// reader emulation
void readData(int packetSize, char *dataIn) {
    union {
        char data[packetSize];
        struct Mystruct packet;
    } dataUnion;
    int i;
    struct Element1 *e1 = NULL;
    struct Element2 *e2 = NULL;

    memcpy(dataUnion.data, dataIn, packetSize);

    printf("Read data e1 size is %d, e2 size is %d\n",
           dataUnion.packet.numofarray1elements, dataUnion.packet.numofarray2elements);
    e1 = malloc(sizeof(struct Element1) * dataUnion.packet.numofarray1elements);
    e2 = malloc(sizeof(struct Element2) * dataUnion.packet.numofarray2elements);
    memcpy(e1, dataUnion.packet.payload, sizeof(struct Element1) *  dataUnion.packet.numofarray1elements);
    memcpy(e2, dataUnion.packet.payload + sizeof(struct Element1) *  dataUnion.packet.numofarray1elements,
           sizeof(struct Element2) *  dataUnion.packet.numofarray2elements);

    for (i = 0; i < dataUnion.packet.numofarray1elements; i++) {
        printf("e1[%d].len = %d, name = %s\n", i, e1[i].len, e1[i].name);
    }

    for (i = 0; i < dataUnion.packet.numofarray2elements; i++) {
        printf("e2[%d].len = %d, name = %s\n", i, e2[i].len, e2[i].name);
    }

}

void main() {
    struct Element1 e1[4];
    struct Element2 e2[8];
    int i;
    int packetSize;
    struct Mystruct *packet = NULL;

    for (i = 0; i < 4; i++) {
        sprintf(e1[i].name, "e1:%d", i);
        e1[i].len = i;
    }
    for (i = 0; i < 8; i++) {
        sprintf(e2[i].name, "e2:%d", i);
        e2[i].len = i;
    }

    // emulated write data
    packetSize = (sizeof(struct Mystruct) + sizeof(e1) + sizeof(e2));
    packet = malloc(packetSize);
    packet->numofarray1elements = 4;
    packet->numofarray2elements = 8;
    memcpy(packet->payload, &e1, sizeof(e1));
    memcpy(packet->payload + sizeof e1, &e2, sizeof(e2));

    // here you do write data, e.g. write(socFd, packet, packetSize);

    // emulate read data
    readData(packetSize, (char*)packet);
}   
0 голосов
/ 11 апреля 2019

Вместо использования структур используйте механизм TLV.Так что для вашего решения: вы можете использовать Тип, Количество Тип, Длина, Значение.

  1. Определить типы, которые известны как на стороне получателя, так и на стороне отправителя.
  2. Определить структуру сообщения какТип занимает 2 байта, Счет типа занимает 2 или 4 байта, Длина занимает 4 байта и Значение.
  3. Это расширяется, так как вы можете добавить любое количество типов в будущем, если тип известен вобе стороны.На стороне получателя, если тип не известен, они могут игнорировать этот TLV.
0 голосов
/ 09 апреля 2019

Не следует отправлять structs по сетевым протоколам (или хранить их в файлах и т. Д.). Вам нужно сериализовать их.

Но чтобы решить вашу проблему, просто измените код отправки:

//this is what your code looks like, I assume:
write(sockFd, myStructVariable, sizeof(struct MyStruct));

до:

//be aware of writev(2) if you want to send these in one system call at once, or copy them into one buffer
write(sockFd, myStructVariable.numofarray1elements, sizeof(int));
write(sockFd, myStructVariable.array1, sizeof(MyArray1) * myStructVariable.numofarray1elements);
write(sockFd, myStructVariable.numofarray2elements, sizeof(int));
write(sockFd, myStructVariable.array2, sizeof(MyArray2) * myStructVariable.numofarray2elements);

или что-то похожее

Затем измените полученный код:

read(sockFd, myStructVariable.numofarray1elements, sizeof(int));
read(sockFd, myStructVariable.array1, sizeof(MyArray1) * myStructVariable.numofarray1elements);
read(sockFd, myStructVariable.numofarray2elements, sizeof(int));
read(sockFd, myStructVariable.array2, sizeof(MyArray2) * myStructVariable.numofarray2elements);

Конечно, вы также можете оставить массив и счетчики, определенные локально, и отправлять их. Убедитесь, что вы проверяете на наличие ошибок, коротких чтений и т. Д.

Взгляните также на flatbuffers , это позволяет вам писать структуры, которые идут с функциями сериализации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...