链表IPC C语言
Linked List IPC C Language
我正在使用守护进程提供简单的服务来嗅探来自任何 ip 的互联网数据包。我遇到了这个问题,我不知道如何将 IP 的链表及其数据包计数从守护程序进程(我将它们存储在链表中)存储到另一个进程(cli)。我上网查找信息,发现我应该使用 IPC,如共享内存、pipe/fifo、配对套接字等。但我不知道如何使用其中的任何一个来将完整链接列表发送到 CLI。你能告诉我,我应该使用哪种 IPC 来完成我的任务吗?以及如何通过 any 传递 LINKED LIST。
要点 - 是可以与我的守护进程交互的 make cli。
链表结构:
typedef struct s_ip
{
uint64_t address;
size_t packets;
struct s_ip *next;
} t_ip;
我只能通过共享内存存储单个变量,如 char*,但不能存储其他变量,如链表或 malloced 结构数组
此外,我应该使用结构数组而不是链表来将数据传输到另一个进程吗?
如果可以的话,举个例子:
守护进程
/* 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 的所有数据包的信息时,我必须将它们发送给他。
谢谢。
不要转移 Next
地址。这无关紧要。只传输需要的信息。您需要建立您将使用的数据格式。您将使用哪种字节顺序?什么位在前,MSB 在前还是 LSB 在前?您将使用什么字符集?二进制流还是可读文本?用特殊字符分隔还是不分隔?压缩?加密?哪种压缩?哪种加密?最后,数据是如何格式化的?如何处理错误?最后,api 会是什么样子?它应该采用 FILE 指针、文件描述符编号、平台相关 input/output 句柄还是函数指针?这些是工程师在设计系统时要回答的问题。
最好是尽可能保持便携(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);
}
}
一方只是在 ip_list_send
中使用简单的 fprintf
调用序列化列表。首先它发送 ASCII 字符 '\x02',它被称为 START OF TEXT
和一个换行符。然后它将用换行符以 ASCII 字符写入的元素计数。然后对于每个 elem 带有换行符的元素。最后 '\x03' 被转移,即。 END OF TEXT
换行。
ip_list_recv
反序列化数据。它使用一个简单的状态机来记住它所处的状态,跟踪计数并分配内存,使用 fscanf 读取数据。该代码可能存在大量错误,恶意攻击者可以利用它。这段代码中的 poll
调用几乎没有用,它只在换行符之后被调用,作为好的代码的种子。好的代码应该通过在每次读取字符后调用 poll
和 fgetc
或更好的 read(..., 1)
来一次读取一个字符并将其添加到缓冲区,从而将一行读入缓冲区,并且所有的对 fscanf
的调用可能是 sscanf(line, ...)
。可能还实现一个 global/function 作用域参数指定的函数超时会很好。当 fprintf
.
需要文件指针时,也可以重写函数以使用文件描述符并使用 fdopen(fd, ...)
和 fclose()
我正在使用守护进程提供简单的服务来嗅探来自任何 ip 的互联网数据包。我遇到了这个问题,我不知道如何将 IP 的链表及其数据包计数从守护程序进程(我将它们存储在链表中)存储到另一个进程(cli)。我上网查找信息,发现我应该使用 IPC,如共享内存、pipe/fifo、配对套接字等。但我不知道如何使用其中的任何一个来将完整链接列表发送到 CLI。你能告诉我,我应该使用哪种 IPC 来完成我的任务吗?以及如何通过 any 传递 LINKED LIST。
要点 - 是可以与我的守护进程交互的 make cli。
链表结构:
typedef struct s_ip
{
uint64_t address;
size_t packets;
struct s_ip *next;
} t_ip;
我只能通过共享内存存储单个变量,如 char*,但不能存储其他变量,如链表或 malloced 结构数组
此外,我应该使用结构数组而不是链表来将数据传输到另一个进程吗?
如果可以的话,举个例子:
守护进程
/* 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 的所有数据包的信息时,我必须将它们发送给他。
谢谢。
不要转移 Next
地址。这无关紧要。只传输需要的信息。您需要建立您将使用的数据格式。您将使用哪种字节顺序?什么位在前,MSB 在前还是 LSB 在前?您将使用什么字符集?二进制流还是可读文本?用特殊字符分隔还是不分隔?压缩?加密?哪种压缩?哪种加密?最后,数据是如何格式化的?如何处理错误?最后,api 会是什么样子?它应该采用 FILE 指针、文件描述符编号、平台相关 input/output 句柄还是函数指针?这些是工程师在设计系统时要回答的问题。
最好是尽可能保持便携(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);
}
}
一方只是在 ip_list_send
中使用简单的 fprintf
调用序列化列表。首先它发送 ASCII 字符 '\x02',它被称为 START OF TEXT
和一个换行符。然后它将用换行符以 ASCII 字符写入的元素计数。然后对于每个 elem 带有换行符的元素。最后 '\x03' 被转移,即。 END OF TEXT
换行。
ip_list_recv
反序列化数据。它使用一个简单的状态机来记住它所处的状态,跟踪计数并分配内存,使用 fscanf 读取数据。该代码可能存在大量错误,恶意攻击者可以利用它。这段代码中的 poll
调用几乎没有用,它只在换行符之后被调用,作为好的代码的种子。好的代码应该通过在每次读取字符后调用 poll
和 fgetc
或更好的 read(..., 1)
来一次读取一个字符并将其添加到缓冲区,从而将一行读入缓冲区,并且所有的对 fscanf
的调用可能是 sscanf(line, ...)
。可能还实现一个 global/function 作用域参数指定的函数超时会很好。当 fprintf
.
fdopen(fd, ...)
和 fclose()