From 200e9154a03986074ff3bd9b76c2b600026548a1 Mon Sep 17 00:00:00 2001 From: p2r3 Date: Fri, 29 Aug 2025 16:11:56 +0300 Subject: [PATCH] implement timeout on stalling connections --- include/globals.h | 3 +++ src/tools.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/globals.h b/include/globals.h index c12e8cd..8a6c8e2 100644 --- a/include/globals.h +++ b/include/globals.h @@ -50,6 +50,9 @@ // only applies to player data. Block changes are written as soon as they // are made, but in much smaller portions. #define DISK_SYNC_INTERVAL 15000000 +// Time in microseconds to spend waiting for data transmission before +// timing out. (default = 5s) +#define NETWORK_TIMEOUT_TIME 5000000 // If defined, scales the frequency at which player movement updates are // broadcast based on the amount of players, reducing overhead for higher // player counts. For very many players, makes movement look jittery. diff --git a/src/tools.c b/src/tools.c index eb4f3ac..a32079a 100644 --- a/src/tools.c +++ b/src/tools.c @@ -36,6 +36,10 @@ ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) { char *p = buf; size_t total = 0; + // Track time of last meaningful network update + // Used to handle timeout when client is stalling + int32_t last_update_time = get_program_time(); + // If requested, exit early when first byte not immediately available if (require_first) { ssize_t r = recv(client_fd, p, 1, MSG_PEEK); @@ -52,6 +56,8 @@ ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) { ssize_t r = recv(client_fd, p + total, n - total, 0); if (r < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { + // handle network timeout + if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) return -1; task_yield(); continue; } else { @@ -64,6 +70,7 @@ ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) { return total; } total += r; + last_update_time = get_program_time(); } total_bytes_received += total; @@ -75,11 +82,16 @@ ssize_t send_all (int client_fd, const void *buf, ssize_t len) { const uint8_t *p = (const uint8_t *)buf; ssize_t sent = 0; + // Track time of last meaningful network update + // Used to handle timeout when client is stalling + int32_t last_update_time = get_program_time(); + // Busy-wait (with task yielding) until all data has been sent while (sent < len) { ssize_t n = send(client_fd, p + sent, len - sent, MSG_NOSIGNAL); if (n > 0) { // some data was sent, log it sent += n; + last_update_time = get_program_time(); continue; } if (n == 0) { // connection was closed, treat this as an error @@ -88,6 +100,8 @@ ssize_t send_all (int client_fd, const void *buf, ssize_t len) { } // not yet ready to transmit, try again if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + // handle network timeout + if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) return -1; task_yield(); continue; }