feat: TCP Server 自动重启机制 — 3种超时条件
触发条件(任一满足即重启): 1. 5分钟无任何连接 (从未连或上次连接后超时) 2. 5分钟无数据交互 (已连接但静默) 3. Auth 密码错误3次 重启流程: 关闭socket→清状态→重新SocketCreat+SocketListen 保护机制: 最多连续重启3次, 超限后进入10分钟冷却期 其他改动: - tcp_json_srv_init 去掉 _init_done 限制, 支持重新初始化 - 新增 tcp_json_restart() 函数 - 新增 g_json_last_comm_ts / g_json_last_connect_ts 追踪 - CONNECT 时清零 restart_count (正常连接重置计数器)
This commit is contained in:
@@ -22,6 +22,9 @@
|
|||||||
#define TCP_JSON_RECV_BUF_LEN (TCP_JSON_MAX_FRAME * 2)
|
#define TCP_JSON_RECV_BUF_LEN (TCP_JSON_MAX_FRAME * 2)
|
||||||
#define TCP_JSON_MAX_PWD_RETRY 3 // Max password retries before lockout
|
#define TCP_JSON_MAX_PWD_RETRY 3 // Max password retries before lockout
|
||||||
#define TCP_JSON_LOCKOUT_TIMEOUT_MS 60000 // 60s lockout after max retries
|
#define TCP_JSON_LOCKOUT_TIMEOUT_MS 60000 // 60s lockout after max retries
|
||||||
|
#define TCP_JSON_IDLE_TIMEOUT_MS 300000 // 5min idle → restart (no connect / no comm)
|
||||||
|
#define TCP_JSON_MAX_RESTART_COUNT 3 // Max auto-restart count
|
||||||
|
#define TCP_JSON_RESTART_COOLDOWN_MS 600000 // 10min cooldown after max restarts
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* Auth / State
|
* Auth / State
|
||||||
@@ -63,6 +66,10 @@ extern TcpJsonAuthState g_json_auth_state;
|
|||||||
extern uint32_t g_json_auth_timer; // ms timer for auth timeout
|
extern uint32_t g_json_auth_timer; // ms timer for auth timeout
|
||||||
extern uint8_t g_json_pwd_retry; // password retry counter
|
extern uint8_t g_json_pwd_retry; // password retry counter
|
||||||
extern TcpJsonPending g_json_pending; // Pending Loop MCU command state
|
extern TcpJsonPending g_json_pending; // Pending Loop MCU command state
|
||||||
|
extern uint32_t g_json_last_comm_ts; // Last communication timestamp (ms)
|
||||||
|
extern uint32_t g_json_last_connect_ts; // Last client connect timestamp (ms)
|
||||||
|
extern uint8_t g_json_restart_count; // Auto-restart counter
|
||||||
|
extern uint32_t g_json_restart_cooldown; // Cooldown deadline (ms)
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* API
|
* API
|
||||||
@@ -71,5 +78,6 @@ void tcp_json_srv_init(void); // Create listen socket on port
|
|||||||
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler
|
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler
|
||||||
void tcp_json_poll(void); // Periodic poll (auth timeout, etc.)
|
void tcp_json_poll(void); // Periodic poll (auth timeout, etc.)
|
||||||
void tcp_json_push_sensor(void); // Push sensor data (0xC0) to TCP client
|
void tcp_json_push_sensor(void); // Push sensor data (0xC0) to TCP client
|
||||||
|
void tcp_json_restart(void); // Close + reopen socket (auto-restart)
|
||||||
|
|
||||||
#endif /* __TCP_JSON_SRV_H__ */
|
#endif /* __TCP_JSON_SRV_H__ */
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ uint8_t g_json_pwd_retry = 0;
|
|||||||
uint32_t g_json_lockout_timer = 0;
|
uint32_t g_json_lockout_timer = 0;
|
||||||
TcpJsonPending g_json_pending = {0, 0, 0, "", 0};
|
TcpJsonPending g_json_pending = {0, 0, 0, "", 0};
|
||||||
uint8_t g_report_active = 0; // 0=不主动上报, 1=主动上报传感器数据
|
uint8_t g_report_active = 0; // 0=不主动上报, 1=主动上报传感器数据
|
||||||
|
uint32_t g_json_last_comm_ts = 0; // 最后通信时间戳
|
||||||
|
uint32_t g_json_last_connect_ts = 0; // 最后连接时间戳
|
||||||
|
uint8_t g_json_restart_count = 0; // 自动重启计数
|
||||||
|
uint32_t g_json_restart_cooldown = 0; // 冷却期截止时间
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* Frame Receive Buffer (line-delimited JSON)
|
* Frame Receive Buffer (line-delimited JSON)
|
||||||
@@ -196,11 +200,12 @@ static void handle_pwd_verify(uint8_t socket, uint32_t msg_id, const char *json)
|
|||||||
} else {
|
} else {
|
||||||
g_json_pwd_retry++;
|
g_json_pwd_retry++;
|
||||||
if (g_json_pwd_retry >= TCP_JSON_MAX_PWD_RETRY) {
|
if (g_json_pwd_retry >= TCP_JSON_MAX_PWD_RETRY) {
|
||||||
g_json_auth_state = JSON_STATE_LOCKOUT;
|
PRINT("JSON: Auth failed %d times → restart\n", g_json_pwd_retry);
|
||||||
g_json_lockout_timer = mstick();
|
// 先发错误再重启,让 TCP 栈有时间 flush
|
||||||
PRINT("JSON: Auth locked out (%d retries)\n", g_json_pwd_retry);
|
|
||||||
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL,
|
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL,
|
||||||
"password incorrect, locked out for 60s");
|
"password incorrect, connection will restart");
|
||||||
|
Delay_Ms(100);
|
||||||
|
tcp_json_restart();
|
||||||
} else {
|
} else {
|
||||||
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL, "password incorrect");
|
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL, "password incorrect");
|
||||||
}
|
}
|
||||||
@@ -788,36 +793,68 @@ static void json_sensor_callback(const uint8_t *pkg, uint16_t len)
|
|||||||
* Public API
|
* Public API
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
|
||||||
|
void tcp_json_restart(void) {
|
||||||
|
PRINT("JSON: === auto-restart (count=%d) ===\n", g_json_restart_count);
|
||||||
|
|
||||||
|
/* 1. 关闭数据 socket 和监听 socket */
|
||||||
|
if (g_json_socket_listen != 0xFF) {
|
||||||
|
WCHNET_SocketClose(g_json_socket_listen + 1, TCP_CLOSE_NORMAL);
|
||||||
|
WCHNET_SocketClose(g_json_socket_listen, TCP_CLOSE_NORMAL);
|
||||||
|
g_json_socket_listen = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. 重置状态 */
|
||||||
|
g_json_auth_state = JSON_STATE_WAIT_AUTH;
|
||||||
|
g_json_pwd_retry = 0;
|
||||||
|
g_json_recv_len = 0;
|
||||||
|
g_json_last_comm_ts = 0;
|
||||||
|
g_json_last_connect_ts = 0;
|
||||||
|
g_json_auth_timer = 0;
|
||||||
|
g_json_lockout_timer = 0;
|
||||||
|
g_json_pending.active = 0;
|
||||||
|
|
||||||
|
/* 3. 重新创建 socket 并监听 */
|
||||||
|
tcp_json_srv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TCP_JSON_SRV_INIT_MAX_RETRY 3
|
||||||
|
|
||||||
void tcp_json_srv_init(void) {
|
void tcp_json_srv_init(void) {
|
||||||
uint8_t ret;
|
uint8_t ret;
|
||||||
SOCK_INF sock_inf;
|
SOCK_INF sock_inf;
|
||||||
static uint8_t _init_done = 0;
|
uint8_t attempt;
|
||||||
|
|
||||||
if (_init_done) return; // 防止 net_srv_init 反复调用
|
for (attempt = 0; attempt < TCP_JSON_SRV_INIT_MAX_RETRY; attempt++) {
|
||||||
_init_done = 1;
|
memset(&sock_inf, 0, sizeof(SOCK_INF));
|
||||||
|
sock_inf.SourPort = TCP_JSON_PORT;
|
||||||
|
sock_inf.ProtoType = PROTO_TYPE_TCP;
|
||||||
|
sock_inf.RecvBufLen = RECE_BUF_LEN;
|
||||||
|
|
||||||
// 注册传感器回调 — 必须在 socket 操作之前,
|
ret = WCHNET_SocketCreat(&g_json_socket_listen, &sock_inf);
|
||||||
// 避免 socket 失败导致 return 而漏掉注册
|
if (ret != WCHNET_ERR_SUCCESS) {
|
||||||
lup_set_sensor_callback(json_sensor_callback);
|
PRINT("JSON: SocketCreat attempt %d failed: 0x%02X\n", attempt + 1, ret);
|
||||||
|
Delay_Ms(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&sock_inf, 0, sizeof(SOCK_INF));
|
ret = WCHNET_SocketListen(g_json_socket_listen);
|
||||||
sock_inf.SourPort = TCP_JSON_PORT;
|
if (ret != WCHNET_ERR_SUCCESS) {
|
||||||
sock_inf.ProtoType = PROTO_TYPE_TCP;
|
PRINT("JSON: SocketListen attempt %d failed: 0x%02X\n", attempt + 1, ret);
|
||||||
sock_inf.RecvBufLen = RECE_BUF_LEN;
|
WCHNET_SocketClose(g_json_socket_listen, TCP_CLOSE_NORMAL);
|
||||||
|
g_json_socket_listen = 0xFF;
|
||||||
|
Delay_Ms(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ret = WCHNET_SocketCreat(&g_json_socket_listen, &sock_inf);
|
// 注册传感器回调(幂等,重复调用无副作用)
|
||||||
if (ret != WCHNET_ERR_SUCCESS) {
|
lup_set_sensor_callback(json_sensor_callback);
|
||||||
PRINT("JSON: SocketCreat failed: 0x%02X\n", ret);
|
g_json_auth_timer = mstick(); // 记录 listen 创建时刻
|
||||||
|
|
||||||
|
PRINT("JSON: TCP listen on port %d (socket %d)\n", TCP_JSON_PORT, g_json_socket_listen);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = WCHNET_SocketListen(g_json_socket_listen);
|
PRINT("JSON: SocketCreat/Listen failed after %d retries\n", TCP_JSON_SRV_INIT_MAX_RETRY);
|
||||||
if (ret != WCHNET_ERR_SUCCESS) {
|
|
||||||
PRINT("JSON: SocketListen failed: 0x%02X\n", ret);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRINT("JSON: TCP listen on port %d (socket %d)\n", TCP_JSON_PORT, g_json_socket_listen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
|
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
|
||||||
@@ -828,6 +865,9 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
|
|||||||
g_json_pwd_retry = 0;
|
g_json_pwd_retry = 0;
|
||||||
g_json_auth_timer = mstick();
|
g_json_auth_timer = mstick();
|
||||||
g_json_recv_len = 0;
|
g_json_recv_len = 0;
|
||||||
|
g_json_last_connect_ts = mstick();
|
||||||
|
g_json_last_comm_ts = mstick();
|
||||||
|
g_json_restart_count = 0; // 有连接就清零重启计数
|
||||||
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
|
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
|
||||||
PRINT("JSON: Client connected on socket %d, recv buf configured\n", socketid);
|
PRINT("JSON: Client connected on socket %d, recv buf configured\n", socketid);
|
||||||
}
|
}
|
||||||
@@ -841,6 +881,7 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
|
|||||||
uint32_t rd_len = recv_len;
|
uint32_t rd_len = recv_len;
|
||||||
uint8_t tmp_buf[RECE_BUF_LEN];
|
uint8_t tmp_buf[RECE_BUF_LEN];
|
||||||
WCHNET_SocketRecv(socketid, tmp_buf, &rd_len);
|
WCHNET_SocketRecv(socketid, tmp_buf, &rd_len);
|
||||||
|
g_json_last_comm_ts = mstick(); // 有数据交互
|
||||||
memcpy(g_json_recv_buf + g_json_recv_len, tmp_buf, (uint16_t)rd_len);
|
memcpy(g_json_recv_buf + g_json_recv_len, tmp_buf, (uint16_t)rd_len);
|
||||||
g_json_recv_len += (uint16_t)rd_len;
|
g_json_recv_len += (uint16_t)rd_len;
|
||||||
|
|
||||||
@@ -1138,7 +1179,38 @@ void tcp_json_poll(void) {
|
|||||||
// Only poll if we have an active connection (socket configured)
|
// Only poll if we have an active connection (socket configured)
|
||||||
if (g_json_socket_listen == 0xFF) return;
|
if (g_json_socket_listen == 0xFF) return;
|
||||||
|
|
||||||
// Auth timeout check
|
// === 自动重启冷却期检查 ===
|
||||||
|
if (g_json_restart_cooldown > 0) {
|
||||||
|
if (mstick() < g_json_restart_cooldown) {
|
||||||
|
// 冷却期内,不做任何操作
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_json_restart_cooldown = 0;
|
||||||
|
g_json_restart_count = 0;
|
||||||
|
PRINT("JSON: restart cooldown expired, resetting counter\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 超时条件1: 5分钟无连接 ===
|
||||||
|
if (g_json_last_connect_ts == 0) {
|
||||||
|
// 从未连接过 — 用 auth_timer (listen 创建时设) 作为起点
|
||||||
|
if (g_json_auth_state == JSON_STATE_WAIT_AUTH
|
||||||
|
&& mstick() - g_json_auth_timer > TCP_JSON_IDLE_TIMEOUT_MS) {
|
||||||
|
PRINT("JSON: %dmin no connection → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
|
||||||
|
goto do_restart;
|
||||||
|
}
|
||||||
|
} else if (mstick() - g_json_last_connect_ts > TCP_JSON_IDLE_TIMEOUT_MS) {
|
||||||
|
PRINT("JSON: %dmin since last connect → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
|
||||||
|
goto do_restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 超时条件2: 5分钟无通信 (仅在有连接/已认证时检查) ===
|
||||||
|
if (g_json_last_comm_ts > 0
|
||||||
|
&& mstick() - g_json_last_comm_ts > TCP_JSON_IDLE_TIMEOUT_MS) {
|
||||||
|
PRINT("JSON: %dmin no communication → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
|
||||||
|
goto do_restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth timeout check (existing)
|
||||||
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
|
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
|
||||||
if (mstick() - g_json_auth_timer > TCP_JSON_AUTH_TIMEOUT_MS) {
|
if (mstick() - g_json_auth_timer > TCP_JSON_AUTH_TIMEOUT_MS) {
|
||||||
PRINT("JSON: Auth timeout, closing connection\n");
|
PRINT("JSON: Auth timeout, closing connection\n");
|
||||||
@@ -1149,7 +1221,7 @@ void tcp_json_poll(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lockout timeout check
|
// Lockout timeout check (existing)
|
||||||
if (g_json_auth_state == JSON_STATE_LOCKOUT) {
|
if (g_json_auth_state == JSON_STATE_LOCKOUT) {
|
||||||
if (mstick() - g_json_lockout_timer > TCP_JSON_LOCKOUT_TIMEOUT_MS) {
|
if (mstick() - g_json_lockout_timer > TCP_JSON_LOCKOUT_TIMEOUT_MS) {
|
||||||
PRINT("JSON: Lockout expired\n");
|
PRINT("JSON: Lockout expired\n");
|
||||||
@@ -1160,6 +1232,19 @@ void tcp_json_poll(void) {
|
|||||||
|
|
||||||
// Check for pending Loop MCU deferred responses
|
// Check for pending Loop MCU deferred responses
|
||||||
json_check_pending();
|
json_check_pending();
|
||||||
|
return;
|
||||||
|
|
||||||
|
do_restart:
|
||||||
|
g_json_restart_count++;
|
||||||
|
g_json_restart_cooldown = 0;
|
||||||
|
if (g_json_restart_count > TCP_JSON_MAX_RESTART_COUNT) {
|
||||||
|
// 超过最大重启次数,进入冷却期
|
||||||
|
g_json_restart_cooldown = mstick() + TCP_JSON_RESTART_COOLDOWN_MS;
|
||||||
|
PRINT("JSON: max restart (%d) reached, cooldown %d min\n",
|
||||||
|
TCP_JSON_MAX_RESTART_COUNT, TCP_JSON_RESTART_COOLDOWN_MS / 60000);
|
||||||
|
} else {
|
||||||
|
tcp_json_restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user