/* 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; }