C структура с массивами данных через сокет - PullRequest
1 голос
/ 13 марта 2020

Программируя приложение в 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;

Я хотел бы отправить RequestPacket по сети, используя send / write, но я не уверен, как обращаться с указателем массива. Если бы я использовал в структуре массив stati c, проблем не было бы, но он должен быть динамическим c, так как я не буду знать rows и cols заранее. Кроме того, указатель не может быть интерпретирован другим процессом, поэтому мне нужно отправить необработанные данные через сокет.

Я уже добавил атрибут __packed__ к этим определениям структуры в попытке устранить набивку. Это здорово, но если мне нужно вылить все данные в линейный буфер, прежде чем я смогу их отправить, тогда какой смысл?

Я задаю этот вопрос, потому что я хотел бы знать, как лучше всего передать эти данные по сети. Заранее спасибо за помощь.

Ответы [ 2 ]

2 голосов
/ 14 марта 2020

Хорошо выполненный код должен минимизировать количество копий. Приведенный ниже подход не выполняет никаких копий в коде пользователя, он не перезаписывает данные, и его можно адаптировать так, чтобы путь с нулевой копией с сетевой карты непосредственно в память приложения.

Эти две структуры также как массив символов, составляют дерево. Объекты - это узлы дерева, указатели на объекты - ребра дерева.

Чтобы передать дерево, вам необходимо пройти его в некотором порядке. Независимо от того, какой это порядок - в ширину или в глубину, или какой-то другой порядок, зависит только от вас, если и передатчик, и получатель оба согласовывают порядок.

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

Например - при условии блокировки кода и написания safe_write для правильной обработки сигналов (большая часть кода) который использует голый write без какой-либо обертки, делает это неправильно - API-интерфейсы POSIX общеизвестно сложно использовать правильно, они усыпляют вас самоуспокоенностью, потому что они, кажется, работают - пока они не будут).

Давайте также решим что наш 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);
}
0 голосов
/ 13 марта 2020
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;
}

тогда вы можете отправить / написать в один звонок

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