如何在不丢失任何数据的情况下优化正在发送的套接字消息的大小?

时间:2019-04-08 18:33:02

标签: c sockets

我的问题是这样的 下面是我必须填充并通过套接字发送的结构

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

这里1个array1成员的大小为1024字节,即array1的总大小= 50 * 1024 = 51200字节 array2的1个成员的大小是500,所以array2的总大小= 12500字节

每当我使用套接字(unix域套接字)的发送api时,我必须发送51200 + 12500 + 4 + 4 = 63708字节

问题是我必须发送整个大小的结构,即使我的结构尺寸很小 numofarray1elements和numofarray2elements 这导致性能问题 在几乎情况下,我的原始数据可能少于10kb,但我每次都发送63k

我无法保留动态数组作为其套接字消息 我已经优化了我的数据结构,array1必须最多包含50个元素 array2最多只能包含25个元素。

现在有什么方法可以发送已填充的确切数据?

请提供一些方法

谢谢

3 个答案:

答案 0 :(得分:1)

实际上,执行此操作的方法是获取可变长度的消息。一种方法是使用大小不确定的单个数组作为结构的最后一个元素。根据消息的类型,它可以用消息或字节表示,例如

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

答案 1 :(得分:0)

使用TLV机制代替使用结构。因此,对于您的解决方案:您可以使用Type,Type Count,Length,Value。

  1. 定义在接收方和发送方都知道的类型
  2. 将您的消息结构定义为 类型占2个字节,类型计数占2或4个字节,长度占4个字节和值。
  3. 这是可扩展的,因为您将来可以添加任意数量的类型,只要双方都知道类型即可。在接收方,如果类型未知,他们可以忽略该TLV。

答案 2 :(得分:-1)

您不应通过网络协议发送structs(或将其存储在文件中,等等)。您需要serialize

但是要解决您的问题,只需更改您的发送代码:

//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,这使您可以编写带有序列化功能的结构