From d0f13d90783ddf311ea617b35272ccda1fabd1cb Mon Sep 17 00:00:00 2001 From: Gregory Gauthier Date: Thu, 26 Mar 2026 14:13:12 +0000 Subject: [PATCH] fixed ansi client --- src/c90/metric_client_ansi.c | 177 +++++++++++++++++++++++++++++++ src/c90/metric_server_fork_sub.c | 84 ++++++++++----- 2 files changed, 232 insertions(+), 29 deletions(-) create mode 100644 src/c90/metric_client_ansi.c diff --git a/src/c90/metric_client_ansi.c b/src/c90/metric_client_ansi.c new file mode 100644 index 0000000..619a198 --- /dev/null +++ b/src/c90/metric_client_ansi.c @@ -0,0 +1,177 @@ +/* metric_client.c - STRICT C89/C90 clean live client with unsub */ +#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_UNSUB 0x07 +#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; + 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 (clean C89 client)\n", PORT); + print_wire_comparison(); + + printf("Commands: ping | get | sub | unsub | quit\n"); + printf("Metrics: cpu, memory, disk, loadavg, uptime\n\n"); + + printf("> "); fflush(stdout); + + maxfd = sock + 1; + + while (1) { + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(sock, &readfds); + + if (select(maxfd, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) continue; + perror("select"); + break; + } + + /* 1. Async message from server */ + 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; + } + + /* Clear current input line */ + printf("\r\x1b[2K"); + + if (type == MSG_PONG) { + printf(" [PONG] server alive\n"); + } else if (type == MSG_METRIC_RESP) { + memcpy(&val, payload, sizeof(double)); + printf(" [METRIC] %.2f\n", val); + } else if (type == MSG_PUSH) { + unsigned char name_len = payload[0]; + payload[1 + name_len] = '\0'; + memcpy(&val, payload + 1 + name_len, sizeof(double)); + printf(" [PUSH] %s: %.2f\n", (char *)payload + 1, val); + } else if (type == MSG_ERROR) { + payload[len] = '\0'; + printf(" [ERROR] %s\n", payload); + } + + printf("> "); fflush(stdout); + } + + /* 2. User input */ + 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) { + const char *metric = line + 4; + send_message(sock, MSG_METRIC_REQ, metric, strlen(metric)); + } + else if (strncmp(line, "sub ", 4) == 0) { + char *metric = strtok(line + 4, " "); + if (metric) { + unsigned int ms = atoi(strtok(NULL, " ")); + unsigned char name_len = strlen(metric); + unsigned char p[BUF_SIZE]; + p[0] = name_len; + memcpy(p + 1, metric, name_len); + p[1 + name_len] = (ms >> 24) & 0xFF; + p[1 + name_len + 1] = (ms >> 16) & 0xFF; + p[1 + name_len + 2] = (ms >> 8) & 0xFF; + p[1 + name_len + 3] = ms & 0xFF; + send_message(sock, MSG_SUBSCRIBE, p, 1 + name_len + 4); + printf(" Subscribed to '%s' every %u ms\n", metric, ms); + } + } + else if (strcmp(line, "unsub") == 0) { + send_message(sock, MSG_UNSUB, NULL, 0); + printf(" Unsubscribed (push stopped)\n"); + } + else if (line[0] != '\0') { + printf(" Unknown command.\n"); + } + + printf("> "); fflush(stdout); + } + } + + close(sock); + printf("\nDisconnected.\n"); + return 0; +} diff --git a/src/c90/metric_server_fork_sub.c b/src/c90/metric_server_fork_sub.c index c949939..52b1ad6 100644 --- a/src/c90/metric_server_fork_sub.c +++ b/src/c90/metric_server_fork_sub.c @@ -1,4 +1,4 @@ -/* metric_server_fork.c - STRICT C89/C90 multi-client metric server with SUBSCRIBE/PUSH */ +/* metric_server_fork.c - STRICT C89/C90 multi-client metric server with SUBSCRIBE/PUSH (fixed) */ #include #include #include @@ -9,6 +9,7 @@ #include #include #include +#include #define PORT 9999 #define BACKLOG 10 @@ -41,7 +42,7 @@ static void send_message(int sock, unsigned char type, header[0] = type; header[1] = (len >> 8) & 0xFF; header[2] = len & 0xFF; - write(sock, header, HEADER_SIZE); + if (write(sock, header, HEADER_SIZE) != HEADER_SIZE) return; if (len > 0) write(sock, payload, len); } @@ -65,14 +66,52 @@ static void handle_client(int client_sock, struct sockaddr_in *addr) unsigned char type; unsigned short len; double val; - char *metric_name; - unsigned int interval_ms; - unsigned char name_len; + fd_set readfds; + struct timeval tv; + int retval; + + /* Subscription state (one active subscription per client for this demo) */ + char subscribed_metric[64] = {0}; + unsigned int interval_ms = 0; + int have_subscription = 0; 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) { + FD_ZERO(&readfds); + FD_SET(client_sock, &readfds); + + /* If we have a subscription, set a timeout for the next PUSH */ + if (have_subscription && interval_ms > 0) { + tv.tv_sec = interval_ms / 1000; + tv.tv_usec = (interval_ms % 1000) * 1000; + retval = select(client_sock + 1, &readfds, NULL, NULL, &tv); + } else { + retval = select(client_sock + 1, &readfds, NULL, NULL, NULL); + } + + if (retval < 0) { + if (errno == EINTR) continue; + perror("select"); + break; + } + + /* Timeout fired → send PUSH */ + if (retval == 0 && have_subscription) { + val = get_metric(subscribed_metric); + if (val >= 0.0) { + unsigned char name_len = strlen(subscribed_metric); + payload[0] = name_len; + memcpy(payload + 1, subscribed_metric, name_len); + memcpy(payload + 1 + name_len, &val, sizeof(double)); + send_message(client_sock, MSG_PUSH, payload, 1 + name_len + sizeof(double)); + printf(" -> PUSH '%s' to %s\n", subscribed_metric, client_ip); + } + continue; + } + + /* Data from client */ if (recv_exact(client_sock, header, HEADER_SIZE) != HEADER_SIZE) break; @@ -100,30 +139,17 @@ static void handle_client(int client_sock, struct sockaddr_in *addr) } } 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); + unsigned char name_len = payload[0]; + if (name_len < sizeof(subscribed_metric)-1) { + memcpy(subscribed_metric, payload + 1, name_len); + subscribed_metric[name_len] = '\0'; } + interval_ms = (payload[1 + name_len] << 24) | + (payload[1 + name_len + 1] << 16) | + (payload[1 + name_len + 2] << 8) | + payload[1 + name_len + 3]; + have_subscription = 1; + printf(" -> SUBSCRIBE '%s' every %u ms from %s\n", subscribed_metric, interval_ms, client_ip); } else { const char *err = "Unknown message type"; @@ -159,7 +185,7 @@ int main(void) perror("listen"); exit(1); } - printf("Metric server (fork() + SUBSCRIBE/PUSH) listening on port %d\n", PORT); + printf("Metric server (fork() + SUBSCRIBE/PUSH with select()) listening on port %d\n", PORT); printf("Available metrics: cpu, memory, disk, loadavg, uptime\n"); printf("Commands: ping, get , sub \n\n");