fix(tcp): recover stalled TCP client connects

This commit is contained in:
2026-05-12 03:31:39 +08:00
parent d36f6b4bee
commit e203db13ca
2 changed files with 57 additions and 8 deletions
+55 -8
View File
@@ -20,6 +20,7 @@ typedef struct {
struct pbuf *hold_pbuf; struct pbuf *hold_pbuf;
uint16_t hold_offset; uint16_t hold_offset;
uint32_t next_retry_ms; uint32_t next_retry_ms;
uint32_t connect_start_ms;
uint8_t index; uint8_t index;
tcp_client_instance_config_t config; tcp_client_instance_config_t config;
tcp_client_status_t status; 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); 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) static void tcp_client_reset_rx_state(tcp_client_ctx_t *ctx)
{ {
if (ctx == NULL) { if (ctx == NULL) {
@@ -51,6 +57,31 @@ static void tcp_client_reset_rx_state(tcp_client_ctx_t *ctx)
ctx->rx_tail = 0u; 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) static void tcp_client_fill_ring_from_pbuf(tcp_client_ctx_t *ctx)
{ {
struct pbuf *q; 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_err(pcb, NULL);
tcp_abort(pcb); tcp_abort(pcb);
ctx->pcb = NULL; ctx->pcb = NULL;
ctx->connect_start_ms = 0u;
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
return ERR_ABRT; return ERR_ABRT;
@@ -147,6 +179,7 @@ static void tcp_client_on_err(void *arg, err_t err)
} }
tcp_client_reset_rx_state(ctx); tcp_client_reset_rx_state(ctx);
ctx->pcb = NULL; ctx->pcb = NULL;
ctx->connect_start_ms = 0u;
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->status.errors++; ctx->status.errors++;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; 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) { if (err != ERR_OK) {
ctx->pcb = NULL; ctx->pcb = NULL;
ctx->connect_start_ms = 0u;
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->status.errors++; ctx->status.errors++;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; 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->pcb = pcb;
ctx->connect_start_ms = 0u;
ctx->status.state = TCP_CLIENT_STATE_CONNECTED; ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
tcp_nagle_disable(pcb); tcp_nagle_disable(pcb);
tcp_arg(pcb, ctx); tcp_arg(pcb, ctx);
@@ -228,6 +263,7 @@ int tcp_client_connect(uint8_t instance)
if (err != ERR_OK) { if (err != ERR_OK) {
tcp_abort(pcb); tcp_abort(pcb);
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->connect_start_ms = 0u;
ctx->status.errors++; ctx->status.errors++;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
return -1; return -1;
@@ -241,6 +277,7 @@ int tcp_client_connect(uint8_t instance)
ctx->config.remote_ip[3]); ctx->config.remote_ip[3]);
ctx->status.state = TCP_CLIENT_STATE_CONNECTING; ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
ctx->connect_start_ms = HAL_GetTick();
tcp_arg(pcb, ctx); tcp_arg(pcb, ctx);
tcp_err(pcb, tcp_client_on_err); tcp_err(pcb, tcp_client_on_err);
err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected); 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_err(pcb, NULL);
tcp_abort(pcb); tcp_abort(pcb);
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->connect_start_ms = 0u;
ctx->status.errors++; ctx->status.errors++;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
return -1; return -1;
@@ -274,6 +312,7 @@ int tcp_client_disconnect(uint8_t instance)
tcp_abort(ctx->pcb); tcp_abort(ctx->pcb);
ctx->pcb = NULL; ctx->pcb = NULL;
} }
ctx->connect_start_ms = 0u;
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
tcp_client_reset_rx_state(ctx); tcp_client_reset_rx_state(ctx);
return 0; 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) void tcp_client_drop(uint8_t instance, uint16_t len)
{ {
tcp_client_ctx_t *ctx; tcp_client_ctx_t *ctx;
uint16_t dropped = 0u; uint16_t acked = 0u;
if (instance >= TCP_CLIENT_INSTANCE_COUNT || len == 0u) { if (instance >= TCP_CLIENT_INSTANCE_COUNT || len == 0u) {
return; return;
} }
ctx = &g_clients[instance]; ctx = &g_clients[instance];
while (dropped < len && ctx->rx_tail != ctx->rx_head) { while (acked < len) {
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE); tcp_client_fill_ring_from_pbuf(ctx);
dropped++; 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) { if (acked > 0u && ctx->pcb != NULL) {
tcp_recved(ctx->pcb, dropped); tcp_recved(ctx->pcb, acked);
} }
tcp_client_fill_ring_from_pbuf(ctx);
} }
bool tcp_client_is_connected(uint8_t instance) bool tcp_client_is_connected(uint8_t instance)
@@ -410,9 +454,12 @@ void tcp_client_poll(void)
continue; continue;
} }
if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) { 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; continue;
} }
if (now >= ctx->next_retry_ms) { if (tick_reached(now, ctx->next_retry_ms)) {
ctx->status.reconnect_count++; ctx->status.reconnect_count++;
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms; ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
(void)tcp_client_connect(i); (void)tcp_client_connect(i);
+2
View File
@@ -16,6 +16,7 @@ extern "C" {
#define TCP_CLIENT_INSTANCE_COUNT 2u #define TCP_CLIENT_INSTANCE_COUNT 2u
#define TCP_CLIENT_RX_BUFFER_SIZE 480u #define TCP_CLIENT_RX_BUFFER_SIZE 480u
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u #define TCP_CLIENT_RECONNECT_DELAY_MS 3000u
#define TCP_CLIENT_CONNECT_TIMEOUT_MS 10000u
typedef enum { typedef enum {
TCP_CLIENT_STATE_IDLE = 0, TCP_CLIENT_STATE_IDLE = 0,
@@ -39,6 +40,7 @@ typedef struct {
uint32_t rx_bytes; uint32_t rx_bytes;
uint32_t tx_bytes; uint32_t tx_bytes;
uint32_t reconnect_count; uint32_t reconnect_count;
uint32_t connect_timeout_count;
uint32_t errors; uint32_t errors;
} tcp_client_status_t; } tcp_client_status_t;