From e203db13cace838d165c3c0c975d9f5b98dbdd6a Mon Sep 17 00:00:00 2001 From: xiao Date: Tue, 12 May 2026 03:31:39 +0800 Subject: [PATCH] fix(tcp): recover stalled TCP client connects --- App/tcp_client.c | 63 ++++++++++++++++++++++++++++++++++++++++++------ App/tcp_client.h | 2 ++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/App/tcp_client.c b/App/tcp_client.c index 26884a3..4f0c2a0 100644 --- a/App/tcp_client.c +++ b/App/tcp_client.c @@ -20,6 +20,7 @@ typedef struct { struct pbuf *hold_pbuf; uint16_t hold_offset; uint32_t next_retry_ms; + uint32_t connect_start_ms; uint8_t index; tcp_client_instance_config_t config; tcp_client_status_t status; @@ -37,6 +38,11 @@ static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size) return (head >= tail) ? (uint16_t)(head - tail) : (uint16_t)(size - tail + head); } +static bool tick_reached(uint32_t now, uint32_t deadline) +{ + return ((int32_t)(now - deadline) >= 0); +} + static void tcp_client_reset_rx_state(tcp_client_ctx_t *ctx) { if (ctx == NULL) { @@ -51,6 +57,31 @@ static void tcp_client_reset_rx_state(tcp_client_ctx_t *ctx) ctx->rx_tail = 0u; } +static void tcp_client_abort_connect_timeout(tcp_client_ctx_t *ctx, uint32_t now) +{ + if (ctx == NULL) { + return; + } + + if (ctx->pcb != NULL) { + tcp_arg(ctx->pcb, NULL); + tcp_recv(ctx->pcb, NULL); + tcp_sent(ctx->pcb, NULL); + tcp_err(ctx->pcb, NULL); + tcp_client_reset_rx_state(ctx); + tcp_abort(ctx->pcb); + ctx->pcb = NULL; + } else { + tcp_client_reset_rx_state(ctx); + } + + ctx->connect_start_ms = 0u; + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->status.errors++; + ctx->status.connect_timeout_count++; + ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms; +} + static void tcp_client_fill_ring_from_pbuf(tcp_client_ctx_t *ctx) { struct pbuf *q; @@ -111,6 +142,7 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, tcp_err(pcb, NULL); tcp_abort(pcb); ctx->pcb = NULL; + ctx->connect_start_ms = 0u; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; return ERR_ABRT; @@ -147,6 +179,7 @@ static void tcp_client_on_err(void *arg, err_t err) } tcp_client_reset_rx_state(ctx); ctx->pcb = NULL; + ctx->connect_start_ms = 0u; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.errors++; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; @@ -162,6 +195,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err) } if (err != ERR_OK) { ctx->pcb = NULL; + ctx->connect_start_ms = 0u; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.errors++; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; @@ -169,6 +203,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err) } ctx->pcb = pcb; + ctx->connect_start_ms = 0u; ctx->status.state = TCP_CLIENT_STATE_CONNECTED; tcp_nagle_disable(pcb); tcp_arg(pcb, ctx); @@ -228,6 +263,7 @@ int tcp_client_connect(uint8_t instance) if (err != ERR_OK) { tcp_abort(pcb); ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->connect_start_ms = 0u; ctx->status.errors++; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; return -1; @@ -241,6 +277,7 @@ int tcp_client_connect(uint8_t instance) ctx->config.remote_ip[3]); ctx->status.state = TCP_CLIENT_STATE_CONNECTING; + ctx->connect_start_ms = HAL_GetTick(); tcp_arg(pcb, ctx); tcp_err(pcb, tcp_client_on_err); err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected); @@ -248,6 +285,7 @@ int tcp_client_connect(uint8_t instance) tcp_err(pcb, NULL); tcp_abort(pcb); ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->connect_start_ms = 0u; ctx->status.errors++; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; return -1; @@ -274,6 +312,7 @@ int tcp_client_disconnect(uint8_t instance) tcp_abort(ctx->pcb); ctx->pcb = NULL; } + ctx->connect_start_ms = 0u; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; tcp_client_reset_rx_state(ctx); return 0; @@ -368,21 +407,26 @@ uint16_t tcp_client_peek(uint8_t instance, uint8_t *data, uint16_t max_len) void tcp_client_drop(uint8_t instance, uint16_t len) { tcp_client_ctx_t *ctx; - uint16_t dropped = 0u; + uint16_t acked = 0u; if (instance >= TCP_CLIENT_INSTANCE_COUNT || len == 0u) { return; } ctx = &g_clients[instance]; - while (dropped < len && ctx->rx_tail != ctx->rx_head) { - ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE); - dropped++; + while (acked < len) { + tcp_client_fill_ring_from_pbuf(ctx); + if (ctx->rx_tail == ctx->rx_head) { + break; + } + while (acked < len && ctx->rx_tail != ctx->rx_head) { + ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE); + acked++; + } } - if (dropped > 0u && ctx->pcb != NULL) { - tcp_recved(ctx->pcb, dropped); + if (acked > 0u && ctx->pcb != NULL) { + tcp_recved(ctx->pcb, acked); } - tcp_client_fill_ring_from_pbuf(ctx); } bool tcp_client_is_connected(uint8_t instance) @@ -410,9 +454,12 @@ void tcp_client_poll(void) continue; } if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) { + if ((uint32_t)(now - ctx->connect_start_ms) >= TCP_CLIENT_CONNECT_TIMEOUT_MS) { + tcp_client_abort_connect_timeout(ctx, now); + } continue; } - if (now >= ctx->next_retry_ms) { + if (tick_reached(now, ctx->next_retry_ms)) { ctx->status.reconnect_count++; ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms; (void)tcp_client_connect(i); diff --git a/App/tcp_client.h b/App/tcp_client.h index bcf027b..93d9f5a 100644 --- a/App/tcp_client.h +++ b/App/tcp_client.h @@ -16,6 +16,7 @@ extern "C" { #define TCP_CLIENT_INSTANCE_COUNT 2u #define TCP_CLIENT_RX_BUFFER_SIZE 480u #define TCP_CLIENT_RECONNECT_DELAY_MS 3000u +#define TCP_CLIENT_CONNECT_TIMEOUT_MS 10000u typedef enum { TCP_CLIENT_STATE_IDLE = 0, @@ -39,6 +40,7 @@ typedef struct { uint32_t rx_bytes; uint32_t tx_bytes; uint32_t reconnect_count; + uint32_t connect_timeout_count; uint32_t errors; } tcp_client_status_t;