Не передавать Next
адрес.Это не имеет значения.Передача только необходимой информации.Вам необходимо установить формат данных, который вы будете использовать.Какую последовательность вы будете использовать?Какой бит первый, MSB первый или LSB первый?Какой набор символов вы будете использовать?Двоичный поток или читаемый текст?Разделены специальным символом или не отделены?Сжатие?Шифрование?Какое сжатие?Какое шифрование?И, наконец, как данные отформатированы?Как обрабатывать ошибки?И, наконец, как будет выглядеть API?Должны ли они принимать указатель FILE, номер дескриптора файла, зависимый от платформы дескриптор ввода / вывода или указатели функций?На эти вопросы инженеры отвечают при проектировании систем.
Лучше всего оставаться максимально переносимым (size_t
не так переносимо, но я оставил его).
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <assert.h>
typedef struct s_ip
{
uint64_t address;
size_t packets;
struct s_ip *next;
} ip_t;
#define IP_LIST_INIT() {0}
void ip_list_elem_init(ip_t *elem, uint64_t address, size_t packets)
{
elem->address = address;
elem->packets = packets;
elem->next = NULL;
}
int ip_list_add(ip_t **head, uint64_t address, size_t packets)
{
if (*head == NULL) {
*head = malloc(sizeof(**head));
if (*head == NULL) return -__LINE__;
ip_list_elem_init(*head, address, packets);
} else {
ip_t *i;
for (i = *head; i->next != NULL; i = i->next) {
continue;
}
i->next = malloc(sizeof(*i->next));
if (i->next == NULL) return -__LINE__;
ip_list_elem_init(i->next, address, packets);
}
return 0;
}
void ip_list_free(ip_t *head)
{
// use system deallocator.... :)
return;
}
int ip_list_send(ip_t *head, FILE *f)
{
char start_of_text = '\x02'; // STX START_OF_TEXT ascii character
char end_of_text = '\x03'; // ETX END_OF_TEXT ascii character
if (fprintf(f, "%c\n", start_of_text) < 0) return -__LINE__;
size_t tmp = 0;
for (ip_t *i = head; i != NULL; i = i->next) {
tmp++;
}
if (fprintf(f, "%zu\n", tmp) < 0) return -__LINE__;
for (ip_t *i = head; i != NULL; i = i->next) {
if (fprintf(f, "%" PRIu64 " %zu\n", i->address, i->packets) < 0) return -__LINE__;
}
if (fprintf(f, "%c\n", end_of_text) < 0) return -__LINE__;
return 0;
}
int ip_list_recv(ip_t **head, FILE *f)
{
if (fcntl(fileno(f), F_SETFL, O_NONBLOCK) < 0) return -__LINE__;
enum {
START_TEXT,
READING_COUNT,
READING_ELEMS,
STOP_TEXT,
END,
} state = START_TEXT;
size_t cnt = 0;
ip_t *prev = NULL;
while (state != END) {
struct pollfd pfd = { .fd = fileno(f), .events = POLLIN };
int pollret = poll(&pfd, 1, 100);
if (pollret < 0) return -__LINE__;
if (pollret == 0) break;
if (pfd.revents != POLLIN) return -__LINE__;
switch (state) {
case START_TEXT: {
char c;
if (fscanf(f, "%c\n", &c) != 1) return -__LINE__; // start of transmission
if (c != '\x02') return -__LINE__;
state = READING_COUNT;
break;
}
case READING_COUNT: {
if (fscanf(f, "%zu\n", &cnt) != 1) return -__LINE__;
state = READING_ELEMS;
break;
}
case READING_ELEMS: {
ip_t *next = malloc(sizeof(*next));
if (next == NULL) return -__LINE__;
if (fscanf(f, "%" SCNu64 " %zu\n", &next->address, &next->packets) != 2) return -__LINE__;
ip_list_elem_init(next, next->address, next->packets);
if (prev) {
prev->next = next;
} else {
*head = next;
}
prev = next;
cnt--;
if (cnt == 0) {
state = STOP_TEXT;
}
break;
}
case STOP_TEXT: {
char c;
if (fscanf(f, "%c\n", &c) != 1) return -__LINE__;
if (c != '\x03') return -__LINE__; // end of transmission
state = END;
break;
}
default:
assert(0);
break;
}
}
return 0;
}
void ip_list_print(ip_t *head)
{
for (ip_t *i = head; i != NULL; i = i->next) {
fprintf(stdout, "%p %" PRIu64 " %zu\n", (void*)i, i->address, i->packets);
}
fprintf(stdout, "\n");
}
int main()
{
int ret;
FILE *f = tmpfile();
if (!f) return -__LINE__;
{
printf("Sending side:\n");
ip_t *head = IP_LIST_INIT();
if (ip_list_add(&head, 1, 1)) return -__LINE__;
if (ip_list_add(&head, 2, 2)) return -__LINE__;
if (ip_list_add(&head, 3, 3)) return -__LINE__;
ip_list_print(head);
if ((ret = ip_list_send(head, f))) return ret;
ip_list_free(head);
}
rewind(f);
{
printf("Receiving side:\n");
ip_t *head = IP_LIST_INIT();
if ((ret = ip_list_recv(&head, f))) return -ret;
ip_list_print(head);
ip_list_free(head);
}
}
Одна сторона просто сериализует список, используя простые fprintf
вызовы в ip_list_send
.Сначала он отправляет символ ASCII '\ x02', который называется START OF TEXT
с новой строкой.Затем количество элементов будет записано в виде символов ASCII с новой строкой.Затем для каждого элемента элемент с новой строки.В конце '\ x03' передается т.е.END OF TEXT
с новой строкой.
ip_list_recv
десериализует данные.Он использует простой конечный автомат для запоминания состояния, в котором он находится, отслеживает количество и выделяет память, использует fscanf для чтения данных.В коде, вероятно, множество ошибок, злоумышленники могут его использовать.Вызов poll
в этом коде в основном бесполезен, он вызывается только после перевода строки, служит семенем хорошего кода.Хороший код должен прочитать строку в буфер, вызвав poll
после каждого прочитанного символа и fgetc
или лучше read(..., 1)
, чтобы прочитать один символ за раз и добавить его в буфер, и все вызовы fscanf
могутбыть sscanf(line, ...)
.Вероятно, было бы неплохо также реализовать глобальный параметр / параметр функции, указанный тайм-аут для функции.Также можно переписать функции для использования файловых дескрипторов и использовать fdopen(fd, ...)
с fclose()
, когда указатель файла необходим для fprintf
.