fix(tcp): recover stalled TCP client connects
This commit is contained in:
+56
-9
@@ -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);
|
|
||||||
dropped++;
|
|
||||||
}
|
|
||||||
if (dropped > 0u && ctx->pcb != NULL) {
|
|
||||||
tcp_recved(ctx->pcb, dropped);
|
|
||||||
}
|
|
||||||
tcp_client_fill_ring_from_pbuf(ctx);
|
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 (acked > 0u && ctx->pcb != NULL) {
|
||||||
|
tcp_recved(ctx->pcb, acked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user