Связанный список IPC C Language - PullRequest
0 голосов
/ 26 ноября 2018

Я делаю простой сервис с демоном для прослушивания интернет-пакетов с любого ip.Я столкнулся с проблемой, заключающейся в том, что я не знаю, как хранить связанный список IP-адресов и их количество пакетов для другого процесса (cli) из процесса демона (где я храню их в связанном списке).Я просмотрел Интернет для получения информации и обнаружил, что должен использовать IPC, например, разделяемую память, pipe / fifo, сокет пары и т. Д. Но я не понимаю, как работать с любым из них для отправки полного списка ссылок в CLI.Подскажите, пожалуйста, какой случай IPC я должен использовать для своей задачи?И как ТОЧНО передать LINKED LIST через любой.

Главное - это make cli, который может взаимодействовать с моим демоном.

Структура связанного списка:

typedef struct s_ip
{
        uint64_t address;
        size_t packets;
        struct s_ip *next;
}               t_ip;    

Я могу хранить через общую память только одну переменную, такую ​​как char *, но ничего другого, например, связанный список или некорректный массив структур

Кроме того, я должен использовать массиввместо связанного списка что-то делает для передачи данных в другой процесс?

Если это возможно, приведите мне такой пример:

DAEMON

/* Daemon code side */
void sendlist_daemon(t_ip *ip_list)
{
    /* code that store linked list */
}

CLI

/* CLI code side */
t_ip *getlist_cli(void)
{
    t_ip *ip_list;

    ip_list = /* here i can get list */
    return (ip_list);
}

Примечание: Демон всегда прослушивает пакеты, поэтому я решил создать связанный список и просто передавать элементы входящими пакетами.Но иногда, когда пользователь из Cli запрашивает информацию обо всех пакетах со всех ips, я должен отправить их ему.

Спасибо.

1 Ответ

0 голосов
/ 27 ноября 2018

Не передавать 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.

...