【问题标题】:C struct with data arrays over a socketC 结构与套接字上的数据数组
【发布时间】:2020-03-13 14:32:05
【问题描述】:

在 C 中编写应用程序时,我想通过网络套接字将以下 struct 数据发送到另一个进程。我的结构定义如下所示:

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;

我想使用send/write 通过网络发送RequestPacket,但我不确定如何处理数组指针。如果我在结构中使用静态数组就没有问题,但它需要是动态的,因为我不会提前知道rowscols。此外,指针不能被其他进程解释,所以我需要通过套接字发送原始数据。

我已经在这些结构定义中添加了__packed__ 属性,试图消除填充。这很好,但如果我需要在发送数据之前将所有数据倒入线性缓冲区,那有什么意义呢?

我问这个问题是因为我想知道通过网络发送这些数据的最佳方法。提前感谢您的帮助。

【问题讨论】:

  • “所以我需要通过套接字发送原始数据”和“我需要将所有数据倒入线性缓冲区中才能发送”-嗯,是的,你回答了你的质疑自己。
  • 有没有办法做到这一点,而不必将所有东西重新打包到线性缓冲区中?换句话说,我已经经历了将数据打包到结构中的麻烦,所以我认为可能有一种可接受的方法来发送它们,即使使用数据指针(即发送已经取消引用数据的语义)。也许这只是天上掉馅饼... :S
  • 我不这么认为:网络不了解您的数据结构,只能处理原始字节,因此您必须发送原始字节(“线性缓冲区”)无论如何。发送指针是毫无意义的,因为在接收机器上它们将指向一个接收程序几乎肯定无法控制的地址。因此,您应该重新打包您的数据结构所需的所有内容(序列化数据结构),发送结果,然后在收到后再次重新打包(反序列化它)。

标签: c sockets struct


【解决方案1】:

性能良好的代码应尽量减少副本。下面的方法在用户代码中不执行复制,它不重写数据,并且它可以适应从网卡直接到应用程序内存的零复制路径。

这两个结构,以及字符数组,构成了一个树。对象是树节点,指向对象的指针是树边。

要传输树,您需要按某种顺序遍历它。是广度优先、深度优先还是其他顺序取决于您 - 只要发送器和接收器都同意该顺序。

按遍历顺序发送结构,不做任何更改。接收者忽略指针,并利用遍历顺序将指针重写为接收端的正确位置。

例如 - 假设阻塞代码,并编写 safe_write 以正确处理信号(大多数使用裸 write 没有一些包装的代码都做错了 - 众所周知,POSIX API 很难正确使用;它们会哄你因为他们似乎工作而自满 - 直到他们不会)。

我们还要确定我们的 API 将正确处理 NULL 指针 - 它会跳过 NULL 指针,并且如果接收到 NULL 指针,接收者将不会期望发送任何内容。

您尚未解决的问题是版本控制:事实上,您定义的大多数对象都没有大小,因此将来无法扩展它们。这可以通过向每个结构添加显式长度字段来解决,以便接收器可以正确地将它们成帧。它只会忽略其他字段。

类型

结构定义可以在 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

发件人

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

接收器

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

【讨论】:

    【解决方案2】:
    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;
    }
    

    然后你可以在一个电话中发送/写

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-06
      相关资源
      最近更新 更多