implement timeout on stalling connections

This commit is contained in:
p2r3
2025-08-29 16:11:56 +03:00
parent ef28d7c0d5
commit 200e9154a0
2 changed files with 17 additions and 0 deletions

View File

@@ -50,6 +50,9 @@
// only applies to player data. Block changes are written as soon as they // only applies to player data. Block changes are written as soon as they
// are made, but in much smaller portions. // are made, but in much smaller portions.
#define DISK_SYNC_INTERVAL 15000000 #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 // If defined, scales the frequency at which player movement updates are
// broadcast based on the amount of players, reducing overhead for higher // broadcast based on the amount of players, reducing overhead for higher
// player counts. For very many players, makes movement look jittery. // player counts. For very many players, makes movement look jittery.

View File

@@ -36,6 +36,10 @@ ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) {
char *p = buf; char *p = buf;
size_t total = 0; 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 requested, exit early when first byte not immediately available
if (require_first) { if (require_first) {
ssize_t r = recv(client_fd, p, 1, MSG_PEEK); 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); ssize_t r = recv(client_fd, p + total, n - total, 0);
if (r < 0) { if (r < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// handle network timeout
if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) return -1;
task_yield(); task_yield();
continue; continue;
} else { } else {
@@ -64,6 +70,7 @@ ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) {
return total; return total;
} }
total += r; total += r;
last_update_time = get_program_time();
} }
total_bytes_received += total; 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; const uint8_t *p = (const uint8_t *)buf;
ssize_t sent = 0; 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 // Busy-wait (with task yielding) until all data has been sent
while (sent < len) { while (sent < len) {
ssize_t n = send(client_fd, p + sent, len - sent, MSG_NOSIGNAL); ssize_t n = send(client_fd, p + sent, len - sent, MSG_NOSIGNAL);
if (n > 0) { // some data was sent, log it if (n > 0) { // some data was sent, log it
sent += n; sent += n;
last_update_time = get_program_time();
continue; continue;
} }
if (n == 0) { // connection was closed, treat this as an error 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 // not yet ready to transmit, try again
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
// handle network timeout
if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) return -1;
task_yield(); task_yield();
continue; continue;
} }