From b6df19eac3d199d58a99689bd3f80d2aa0af9fa8 Mon Sep 17 00:00:00 2001 From: Gregory Gauthier Date: Thu, 26 Mar 2026 13:44:51 +0000 Subject: [PATCH] add forking versions and subscribe feature --- .gitignore | 1 + metric_client_sub | Bin 0 -> 34416 bytes metric_server_fork_sub | Bin 0 -> 34600 bytes src/c90/metric_client.c | 92 ++++++++++----- src/c90/metric_client_sub.c | 173 ++++++++++++++++++++++++++++ src/c90/metric_server_fork.c | 26 +++-- src/c90/metric_server_fork_sub.c | 191 +++++++++++++++++++++++++++++++ 7 files changed, 440 insertions(+), 43 deletions(-) create mode 100755 metric_client_sub create mode 100755 metric_server_fork_sub create mode 100644 src/c90/metric_client_sub.c create mode 100644 src/c90/metric_server_fork_sub.c diff --git a/.gitignore b/.gitignore index 9f11b75..41f7f34 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +build/ diff --git a/metric_client_sub b/metric_client_sub new file mode 100755 index 0000000000000000000000000000000000000000..1c1b1db7e536dc41dc9147f049b4e2c495ac70eb GIT binary patch literal 34416 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 literal 0 HcmV?d00001 diff --git a/src/c90/metric_client.c b/src/c90/metric_client.c index 7f1a929..fdd4d5d 100644 --- a/src/c90/metric_client.c +++ b/src/c90/metric_client.c @@ -1,4 +1,4 @@ -/* metric_client.c - C89/C90 custom binary metric client */ +/* metric_client.c - STRICT C89/C90 custom binary metric client */ #include #include #include @@ -7,10 +7,10 @@ #include #include -#define PORT 9999 +#define PORT 9999 #define BUF_SIZE 1024 -/* same constants */ +/* Protocol constants (must match server) */ #define MSG_PING 0x01 #define MSG_PONG 0x02 #define MSG_METRIC_REQ 0x03 @@ -19,92 +19,122 @@ #define HEADER_SIZE 3 -static void send_message(int sock, unsigned char type, const void *payload, unsigned short len) { +static void send_message(int sock, unsigned char type, + const void *payload, unsigned short len) +{ unsigned char header[HEADER_SIZE]; header[0] = type; - header[1] = (len >> 8) & 0xFF; + header[1] = (len >> 8) & 0xFF; /* big-endian */ header[2] = len & 0xFF; write(sock, header, HEADER_SIZE); - if (len > 0) write(sock, payload, len); + if (len > 0) + write(sock, payload, len); } -static int recv_exact(int sock, void *buf, int n) { +static int recv_exact(int sock, void *buf, int n) +{ int total = 0; while (total < n) { - int r = read(sock, (char*)buf + total, n - total); + int r = read(sock, (char *)buf + total, n - total); if (r <= 0) return -1; total += r; } return total; } -static void print_wire_comparison(void) { +static void print_wire_comparison(void) +{ printf("\n--- Wire overhead comparison ---\n"); printf("Our protocol (METRIC_REQ 'cpu'): %d bytes\n", HEADER_SIZE + 3); printf("Equivalent HTTP GET: 77 bytes\n"); printf("HTTP is ~13x larger\n\n"); } -int main(void) { +int main(void) +{ int sock; struct sockaddr_in addr; unsigned char header[HEADER_SIZE]; unsigned char payload[BUF_SIZE]; double val; char line[256]; + unsigned char type; + unsigned short len; + const char *metric; sock = socket(AF_INET, SOCK_STREAM, 0); - addr.sin_family = AF_INET; - addr.sin_port = htons(PORT); - inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); - - if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { - perror("connect"); + if (sock < 0) { + perror("socket"); exit(1); } - printf("Connected to 127.0.0.1:%d\n", PORT); + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT); + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("connect"); + close(sock); + exit(1); + } + + printf("Connected to 127.0.0.1:%d (STRICT C89 client)\n", PORT); print_wire_comparison(); - printf("Commands: ping | get | quit\nMetrics: cpu, memory, disk, loadavg, uptime\n\n"); + printf("Commands: ping | get | quit\n"); + printf("Metrics: cpu, memory, disk, loadavg, uptime\n\n"); while (1) { printf("> "); - if (fgets(line, sizeof(line), stdin) == NULL) break; + if (fgets(line, sizeof(line), stdin) == NULL) + break; + line[strcspn(line, "\n")] = '\0'; - if (strcmp(line, "quit") == 0) break; - if (strcmp(line, "ping") == 0) { + if (strcmp(line, "quit") == 0) { + break; + } + else if (strcmp(line, "ping") == 0) { send_message(sock, MSG_PING, NULL, 0); - } else if (strncmp(line, "get ", 4) == 0) { - const char *metric = line + 4; + } + else if (strncmp(line, "get ", 4) == 0) { + metric = line + 4; send_message(sock, MSG_METRIC_REQ, metric, strlen(metric)); - } else { + } + else { printf(" Unknown command.\n"); continue; } /* blocking receive */ - if (recv_exact(sock, header, HEADER_SIZE) != HEADER_SIZE) break; - unsigned char type = header[0]; - unsigned short len = (header[1] << 8) | header[2]; + if (recv_exact(sock, header, HEADER_SIZE) != HEADER_SIZE) + break; + + type = header[0]; + len = (header[1] << 8) | header[2]; if (len > 0) { - if (recv_exact(sock, payload, len) != len) break; + if (recv_exact(sock, payload, len) != len) + break; } if (type == MSG_PONG) { printf(" [PONG] server alive\n"); - } else if (type == MSG_METRIC_RESP) { + } + else if (type == MSG_METRIC_RESP) { memcpy(&val, payload, sizeof(double)); printf(" [METRIC] %.2f\n", val); - } else if (type == MSG_ERROR) { + } + else if (type == MSG_ERROR) { payload[len] = '\0'; printf(" [ERROR] %s\n", payload); } + else { + printf(" [???] Unknown message type 0x%02X\n", type); + } } close(sock); printf("Disconnected.\n"); return 0; -} \ No newline at end of file +} diff --git a/src/c90/metric_client_sub.c b/src/c90/metric_client_sub.c new file mode 100644 index 0000000..57a77b7 --- /dev/null +++ b/src/c90/metric_client_sub.c @@ -0,0 +1,173 @@ +/* metric_client.c - STRICT C89/C90 client with live SUBSCRIBE/PUSH using select() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 9999 +#define BUF_SIZE 1024 + +#define MSG_PING 0x01 +#define MSG_PONG 0x02 +#define MSG_METRIC_REQ 0x03 +#define MSG_METRIC_RESP 0x04 +#define MSG_SUBSCRIBE 0x05 +#define MSG_PUSH 0x06 +#define MSG_ERROR 0xFF + +#define HEADER_SIZE 3 + +static void send_message(int sock, unsigned char type, + const void *payload, unsigned short len) +{ + unsigned char header[HEADER_SIZE]; + header[0] = type; + header[1] = (len >> 8) & 0xFF; + header[2] = len & 0xFF; + write(sock, header, HEADER_SIZE); + if (len > 0) + write(sock, payload, len); +} + +static int recv_exact(int sock, void *buf, int n) +{ + int total = 0; + while (total < n) { + int r = read(sock, (char *)buf + total, n - total); + if (r <= 0) return -1; + total += r; + } + return total; +} + +static void print_wire_comparison(void) +{ + printf("\n--- Wire overhead comparison ---\n"); + printf("Our protocol (METRIC_REQ 'cpu'): %d bytes\n", HEADER_SIZE + 3); + printf("Equivalent HTTP GET: 77 bytes\n"); + printf("HTTP is ~13x larger\n\n"); +} + +int main(void) +{ + int sock, maxfd; + struct sockaddr_in addr; + unsigned char header[HEADER_SIZE]; + unsigned char payload[BUF_SIZE]; + double val; + char line[256]; + unsigned char type; + unsigned short len; + const char *metric; + unsigned int interval_ms; + unsigned char name_len; + fd_set readfds; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { perror("socket"); exit(1); } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT); + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("connect"); close(sock); exit(1); + } + + printf("Connected to 127.0.0.1:%d (STRICT C89 live client)\n", PORT); + print_wire_comparison(); + + printf("Commands: ping | get | sub | quit\n"); + printf("Metrics: cpu, memory, disk, loadavg, uptime\n\n"); + + maxfd = sock > 0 ? sock : 0; + + while (1) { + FD_ZERO(&readfds); + FD_SET(0, &readfds); /* stdin */ + FD_SET(sock, &readfds); /* socket */ + + if (select(maxfd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) continue; + perror("select"); + break; + } + + /* 1. Data ready on the socket → handle PUSH / response immediately */ + if (FD_ISSET(sock, &readfds)) { + if (recv_exact(sock, header, HEADER_SIZE) != HEADER_SIZE) break; + + type = header[0]; + len = (header[1] << 8) | header[2]; + + if (len > 0) { + if (recv_exact(sock, payload, len) != len) break; + } + + if (type == MSG_PONG) { + printf("\n [PONG] server alive\n"); + } + else if (type == MSG_METRIC_RESP) { + memcpy(&val, payload, sizeof(double)); + printf("\n [METRIC] %.2f\n", val); + } + else if (type == MSG_PUSH) { + name_len = payload[0]; + payload[1 + name_len] = '\0'; + memcpy(&val, payload + 1 + name_len, sizeof(double)); + printf("\n [PUSH] %s: %.2f\n", (char *)payload + 1, val); + } + else if (type == MSG_ERROR) { + payload[len] = '\0'; + printf("\n [ERROR] %s\n", payload); + } + else { + printf("\n [???] Unknown type 0x%02X\n", type); + } + printf("> "); fflush(stdout); /* re-print prompt after async message */ + } + + /* 2. User typed something on stdin */ + if (FD_ISSET(0, &readfds)) { + if (fgets(line, sizeof(line), stdin) == NULL) break; + line[strcspn(line, "\n")] = '\0'; + + if (strcmp(line, "quit") == 0) break; + else if (strcmp(line, "ping") == 0) { + send_message(sock, MSG_PING, NULL, 0); + } + else if (strncmp(line, "get ", 4) == 0) { + metric = line + 4; + send_message(sock, MSG_METRIC_REQ, metric, strlen(metric)); + } + else if (strncmp(line, "sub ", 4) == 0) { + metric = strtok(line + 4, " "); + if (metric) { + interval_ms = atoi(strtok(NULL, " ")); + name_len = strlen(metric); + payload[0] = name_len; + memcpy(payload + 1, metric, name_len); + payload[1 + name_len] = (interval_ms >> 24) & 0xFF; + payload[1 + name_len + 1] = (interval_ms >> 16) & 0xFF; + payload[1 + name_len + 2] = (interval_ms >> 8) & 0xFF; + payload[1 + name_len + 3] = interval_ms & 0xFF; + send_message(sock, MSG_SUBSCRIBE, payload, 1 + name_len + 4); + printf(" Subscribed to '%s' every %u ms\n", metric, interval_ms); + } + } + else { + printf(" Unknown command.\n"); + } + printf("> "); fflush(stdout); + } + } + + close(sock); + printf("\nDisconnected.\n"); + return 0; +} diff --git a/src/c90/metric_server_fork.c b/src/c90/metric_server_fork.c index b125dbe..1c5d43c 100644 --- a/src/c90/metric_server_fork.c +++ b/src/c90/metric_server_fork.c @@ -1,4 +1,4 @@ -/* metric_server_fork.c - C89/C90 multi-client metric server using fork() */ +/* metric_server_fork.c - STRICT C89/C90 multi-client metric server using fork() */ #include #include #include @@ -58,9 +58,12 @@ static int recv_exact(int sock, void *buf, int n) static void handle_client(int client_sock, struct sockaddr_in *addr) { - unsigned char header[HEADER_SIZE]; + unsigned char header[HEADER_SIZE]; /* all declarations first */ unsigned char payload[BUF_SIZE]; char client_ip[INET_ADDRSTRLEN]; + unsigned char type; + unsigned short len; + double val; inet_ntop(AF_INET, &addr->sin_addr, client_ip, sizeof(client_ip)); printf("[+] Connected from %s:%d\n", client_ip, ntohs(addr->sin_port)); @@ -69,8 +72,8 @@ static void handle_client(int client_sock, struct sockaddr_in *addr) if (recv_exact(client_sock, header, HEADER_SIZE) != HEADER_SIZE) break; - unsigned char type = header[0]; - unsigned short len = (header[1] << 8) | header[2]; + type = header[0]; + len = (header[1] << 8) | header[2]; if (len > 0) { if (recv_exact(client_sock, payload, len) != len) @@ -83,7 +86,7 @@ static void handle_client(int client_sock, struct sockaddr_in *addr) } else if (type == MSG_METRIC_REQ) { payload[len] = '\0'; - double val = get_metric((char *)payload); + val = get_metric((char *)payload); printf(" <- METRIC_REQ '%s' from %s\n", (char *)payload, client_ip); if (val < 0.0) { const char *err = "Unknown metric"; @@ -111,7 +114,6 @@ int main(void) srand(time(NULL)); - /* Classic Unix trick to reap children automatically (no zombies) */ signal(SIGCHLD, SIG_IGN); srv_sock = socket(AF_INET, SOCK_STREAM, 0); @@ -147,20 +149,20 @@ int main(void) } pid = fork(); - if (pid < 0) { /* fork failed */ + if (pid < 0) { perror("fork"); close(client_sock); } - else if (pid == 0) { /* CHILD process */ - close(srv_sock); /* child does not need the listening socket */ + else if (pid == 0) { /* CHILD */ + close(srv_sock); handle_client(client_sock, &client_addr); exit(0); } - else { /* PARENT process */ - close(client_sock); /* parent does not need the client socket */ + else { /* PARENT */ + close(client_sock); } } close(srv_sock); return 0; -} \ No newline at end of file +} diff --git a/src/c90/metric_server_fork_sub.c b/src/c90/metric_server_fork_sub.c new file mode 100644 index 0000000..a496356 --- /dev/null +++ b/src/c90/metric_server_fork_sub.c @@ -0,0 +1,191 @@ +/* metric_server_fork_sub.c - STRICT C89/C90 multi-client metric server with SUBSCRIBE/PUSH */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 9999 +#define BACKLOG 10 +#define BUF_SIZE 1024 + +#define MSG_PING 0x01 +#define MSG_PONG 0x02 +#define MSG_METRIC_REQ 0x03 +#define MSG_METRIC_RESP 0x04 +#define MSG_SUBSCRIBE 0x05 +#define MSG_PUSH 0x06 +#define MSG_ERROR 0xFF + +#define HEADER_SIZE 3 + +static double get_metric(const char *name) +{ + if (strcmp(name, "cpu") == 0) return 5.0 + (rand() % 9000)/100.0; + if (strcmp(name, "memory") == 0) return 30.0 + (rand() % 5000)/100.0; + if (strcmp(name, "disk") == 0) return 40.0 + (rand() % 5000)/100.0; + if (strcmp(name, "loadavg") == 0) return 0.5 + (rand() % 350)/100.0; + if (strcmp(name, "uptime") == 0) return (double)(time(NULL) % 100000); + return -1.0; +} + +static void send_message(int sock, unsigned char type, + const void *payload, unsigned short len) +{ + unsigned char header[HEADER_SIZE]; + header[0] = type; + header[1] = (len >> 8) & 0xFF; + header[2] = len & 0xFF; + write(sock, header, HEADER_SIZE); + if (len > 0) + write(sock, payload, len); +} + +static int recv_exact(int sock, void *buf, int n) +{ + int total = 0; + while (total < n) { + int r = read(sock, (char *)buf + total, n - total); + if (r <= 0) return -1; + total += r; + } + return total; +} + +static void handle_client(int client_sock, struct sockaddr_in *addr) +{ + unsigned char header[HEADER_SIZE]; + unsigned char payload[BUF_SIZE]; + char client_ip[INET_ADDRSTRLEN]; + unsigned char type; + unsigned short len; + double val; + char *metric_name; + unsigned int interval_ms; + unsigned char name_len; + + inet_ntop(AF_INET, &addr->sin_addr, client_ip, sizeof(client_ip)); + printf("[+] Connected from %s:%d\n", client_ip, ntohs(addr->sin_port)); + + while (1) { + if (recv_exact(client_sock, header, HEADER_SIZE) != HEADER_SIZE) + break; + + type = header[0]; + len = (header[1] << 8) | header[2]; + + if (len > 0) { + if (recv_exact(client_sock, payload, len) != len) + break; + } + + if (type == MSG_PING) { + printf(" <- PING from %s\n", client_ip); + send_message(client_sock, MSG_PONG, NULL, 0); + } + else if (type == MSG_METRIC_REQ) { + payload[len] = '\0'; + val = get_metric((char *)payload); + printf(" <- METRIC_REQ '%s' from %s\n", (char *)payload, client_ip); + if (val < 0.0) { + const char *err = "Unknown metric"; + send_message(client_sock, MSG_ERROR, err, strlen(err)); + } else { + send_message(client_sock, MSG_METRIC_RESP, &val, sizeof(double)); + } + } + else if (type == MSG_SUBSCRIBE) { + name_len = payload[0]; + payload[name_len + 1] = '\0'; + metric_name = (char *)payload + 1; + interval_ms = (payload[name_len + 1] << 24) | + (payload[name_len + 2] << 16) | + (payload[name_len + 3] << 8) | + payload[name_len + 4]; + printf(" -> SUBSCRIBE '%s' every %u ms from %s\n", metric_name, interval_ms, client_ip); + + /* Simple push loop inside this child process */ + while (1) { + val = get_metric(metric_name); + if (val < 0.0) break; + + name_len = strlen(metric_name); + payload[0] = name_len; + memcpy(payload + 1, metric_name, name_len); + memcpy(payload + 1 + name_len, &val, sizeof(double)); + + send_message(client_sock, MSG_PUSH, + payload, 1 + name_len + sizeof(double)); + + usleep(interval_ms * 1000); + } + } + else { + const char *err = "Unknown message type"; + send_message(client_sock, MSG_ERROR, err, strlen(err)); + } + } + + close(client_sock); + printf("[-] Disconnected from %s\n", client_ip); +} + +int main(void) +{ + int srv_sock, client_sock; + struct sockaddr_in server_addr, client_addr; + socklen_t client_len = sizeof(client_addr); + pid_t pid; + + srand(time(NULL)); + signal(SIGCHLD, SIG_IGN); + + srv_sock = socket(AF_INET, SOCK_STREAM, 0); + if (srv_sock < 0) { perror("socket"); exit(1); } + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT); + + if (bind(srv_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + perror("bind"); exit(1); + } + if (listen(srv_sock, BACKLOG) < 0) { + perror("listen"); exit(1); + } + + printf("Metric server (fork() + SUBSCRIBE/PUSH) listening on port %d\n", PORT); + printf("Available metrics: cpu, memory, disk, loadavg, uptime\n"); + printf("Commands: ping, get , sub \n\n"); + + while (1) { + client_sock = accept(srv_sock, (struct sockaddr *)&client_addr, &client_len); + if (client_sock < 0) { + if (errno == EINTR) continue; + perror("accept"); + continue; + } + + pid = fork(); + if (pid < 0) { + perror("fork"); + close(client_sock); + } + else if (pid == 0) { + close(srv_sock); + handle_client(client_sock, &client_addr); + exit(0); + } + else { + close(client_sock); + } + } + + close(srv_sock); + return 0; +}