简体   繁体   English

带有套接字数据数组的 C 结构

[英]C struct with data arrays over a socket

Programming an application in C, I have the following struct data that I'd like to send over a network socket to another process.用 C 编程应用程序,我有以下struct数据,我想通过网络套接字将其发送到另一个进程。 My struct definitions are shown below:我的结构定义如下所示:

typedef struct __attribute__((__packed__)) Matrix {
    uint32_t rows;
    uint32_t cols;
    char * matrix;
} matrix_s;

typedef struct __attribute__((__packed__)) RequestPacket {
    uint32_t request_code;
    matrix_s * mat;
} request_packet_s;

I'd like to send the RequestPacket over the network using send / write , but I'm unsure how to handle the array pointer.我想使用send / write通过网络发送RequestPacket ,但我不确定如何处理数组指针。 If I were using a static array in the struct there would be no problem, but it needs to be dynamic since I won't know rows and cols ahead of time.如果我使用的结构的静态数组就没有问题,但它需要是动态的,因为我不知道rowscols的时间提前。 Furthermore, the pointer can't be interpreted by the other process, so I need to send the raw data over the socket.此外,指针不能被其他进程解释,所以我需要通过套接字发送原始数据。

I've already added the __packed__ attribute to these struct definitions, in an attempt to eliminate padding.我已经将__packed__属性添加到这些结构定义中,以尝试消除填充。 That's great, but if I need to pour all of the data into a linear buffer before being able to send it, then what was the point?太好了,但是如果我需要在发送之前将所有数据倒入线性缓冲区,那有什么意义呢?

I'm asking this question because I'd like to know the best approach in sending this data over the network.我问这个问题是因为我想知道通过网络发送这些数据的最佳方法。 Thanks in advance for the help.在此先感谢您的帮助。

Well-perfoming code should minimize copies.性能良好的代码应该尽量减少副本。 The approach below performs no copies in user code, it doesn't rewrite data, and it could be adapted to have a zero-copy path from the network card directly into application memory.下面的方法在用户代码中不执行任何复制,它不会重写数据,并且它可以适用于从网卡直接到应用程序内存的零复制路径。

These two structures, as well as the character array, constitute a tree.这两个结构体,以及字符数组,构成了一棵树。 The objects are tree nodes, the pointers-to-objects are tree edges.对象是树节点,指向对象的指针是树边。

To transmit the tree, you need to traverse it in some order.要传输树,您需要按某种顺序遍历它。 Whether it's breadth-first or depth-first or some other order is up to you - as long as the transmitter and the receiver both agree on the order.是广度优先还是深度优先还是其他顺序取决于您 - 只要发射器和接收器都同意该顺序。

Send structures in traversal order, without any changes.按遍历顺序发送结构,不做任何更改。 The receiver ignores the pointers, and leverages the traversal order to rewrite the pointers to correct locations on the receive side.接收器忽略指针,并利用遍历顺序将指针重写为接收端的正确位置。

For example - assuming blocking code, and having written safe_write to properly deal with signals (most code that uses naked write without some wrapper is doing it wrong - POSIX APIs are notoriously hard to use correctly; they lull you into complacency since they appear to work - until they won't).例如 - 假设阻塞代码,并编写了safe_write来正确处理信号(大多数使用没有包装器的裸write代码做错了 - 众所周知,POSIX API 很难正确使用;它们让你自满,因为它们似乎可以工作- 直到他们不会)。

Let's also decide that our API will correctly deal with NULL pointers - it will skip NULL pointers, and the receiver will not expect anything sent if a NULL pointer was received.让我们也决定我们的 API 将正确处理 NULL 指针 - 它会跳过 NULL 指针,如果接收到 NULL 指针,接收器将不会期望发送任何内容。

The problem you haven't addressed is versioning: as it is, most objects you defined carry no size, so they cannot be expanded in the future.您尚未解决的问题是版本控制:实际上,您定义的大多数对象都没有大小,因此将来无法扩展它们。 This may be addressed by adding an explicit length field to each structure, so that the receiver can correctly frame them.这可以通过向每个结构添加显式长度字段来解决,以便接收器可以正确地将它们成帧。 It'd simply ignore the additional fields.它会简单地忽略附加字段。

The Types类型

The structure definitions had been made portable between 32- and 64-bit platforms.结构定义可以在 32 位和 64 位平台之间移植。

typedef unsigned char BOOL;
enum { FALSE, TRUE };
#define PTR(type, name) union { type *name; uint64_t name##_; }

#define MAX_MATRIX_SIZE 128*128
typedef struct __attribute__((__packed__)) Matrix {
    uint32_t rows;
    uint32_t cols;
    PTR(char, matrix);
} matrix_s;

typedef struct __attribute__((__packed__)) RequestPacket {
    uint32_t request_code;
    PTR(matrix_s, mat);
    PTR(matrix_s, opt_kernel);
} request_packet_s;

#undef PTR

Sender发件人

BOOL safe_write(int fd, void *buf, size_t length) {
  // Returns TRUE if buf is NULL or if writing had succeeded, FALSE on error
  if (!buf) return TRUE;
  assert(length);
  // TODO
  return FALSE;
}

BOOL sendBytes(int fd, char *bytes, size_t length) {
  if (!bytes) return TRUE;
  assert(length); // Sending a non-NULL pointer for a zero-sized block
                  // we shall declare to be a protocol error. Since
                  // this is the send side, this is a bug.
  return safe_write(fd, bytes, length);
}

BOOL sendMatrix(int fd, matrix_s *mat) {
  if (!mat) return TRUE;
  if (!safe_write(fd, mat, sizeof(*mat))) return FALSE;
  return sendBytes(fd, mat->matrix, mat->rows * mat->cols);
}

BOOL sendRequestPacket(int fd, request_packet_s *req) {
  if (!req) return TRUE;
  if (!safe_write(fd, req, sizeof(*req))) return FALSE;
  if (!sendMatrix(fd, req->mat)) return FALSE;
  return sendMatrix(fd, req->opt_kernel);
}

Receiver接收者

BOOL safe_read(int fd, void *buf, size_t length) {
  // Returns TRUE if reading had succeeded, FALSE on error
  if (!buf) return TRUE;
  if (!length) return FALSE; // Protocol error on receive: this is a
                             // data validation failure, and must be handled
                             // like any other error.
  // TODO
  return FALSE;
}

static inline BOOL free_read(void **const ptr) {
  free(*ptr);
  *ptr = NULL;
  return FALSE;
}

BOOL malloc_read(int fd, void **const buf, size_t length) {
  // This should be using an arena allocator, really.
  if (!*buf) return TRUE;
  if (!length) return FALSE;
  *buf = malloc(length);
  if (!*buf) return FALSE;
  if (!safe_read(fd, *buf, length)) return free_read(buf);
  return TRUE;
}

BOOL recvMatrix(int fd, matrix_s **const mat) {
  if (!malloc_read(fd, mat, sizeof(**mat))) return FALSE;
  size_t size = (*mat)->rows * (*mat)->cols;
  if (size > MAX_MATRIX_SIZE) goto error;
  if (size)
    if (!malloc_read(fd, &(*mat)->matrix, size)) goto error;
  return TRUE;
error:
  return free_read(mat);
}

BOOL recvRequestPacket(int fd, request_packet_s **const req) {
  if (!malloc_read(fd, req, sizeof(**req))) return FALSE;
  if (!recvMatrix(fd, &(*req)->mat)) goto error1;
  if (!recvMatrix(fd, &(*req)->opt_kernel)) goto error2;
  return TRUE;
error2:
  free_read(&(*req)->mat);
error1:
  return free_read(req);
}
typedef struct __attribute__((__packed__)) Matrix {
    uint32_t rows;
    uint32_t cols;
    char matrix[];
} matrix_s;

typedef struct __attribute__((__packed__)) RequestPacket {
    uint32_t request_code;
    matrix_s mat;
} request_packet_s;


request_packet_s *allocreq(uint32_t req, size_t rows. size_t cols);
{
      request_packet_s *p = malloc(sizeof(*p) + rows * cols);
      /* init it - d osomething */
      return p;
}

then you can send /write in one call然后你可以在一个电话中发送/写入

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM