基于LWIP Netconn API的TCP连接优雅关闭与重连问题排查
FreeRTOS v10.0.0 + LwIP 2.1.2 Netconn API TCP优雅关闭与重连问题解决
问题原因分析
- 当Linux端Netcat关闭时,TCP连接已被动断开,此时调用
netconn_close()返回ERR_CONN(连接不存在)属于正常现象。 netconn_delete()返回ERR_INPROGRESS是因为LwIP内部仍有未完成的TCP协议栈操作(如连接处于FIN_WAIT状态),无法立即释放资源。- 重连时
netconn_bind()返回ERR_USE,核心原因是原监听用的tcpListenNetconn未被正确释放,端口一直被占用;其次是TCP端口关闭后可能处于TIME_WAIT状态,默认无法立即复用。
解决方案
1. 修正连接异常处理流程
- 先判断错误类型,避免对已断开的连接调用
netconn_close()。 - 处理
netconn_delete()的ERR_INPROGRESS错误,通过循环等待确保资源释放完成。 - 必须释放监听用的
netconn资源,避免端口占用。
2. 实现TCP优雅关闭
- 主动关闭时,先调用
netconn_shutdown()关闭读写通道,再执行关闭和删除操作;被动关闭时直接处理资源释放。
3. 解决端口占用问题
在lwipopts.h中开启TCP端口复用配置:
#define TCP_REUSE 1 // 允许复用处于TIME_WAIT状态的端口 #define TCP_TIMEWAIT_MSL 2000 // 缩短TIME_WAIT时长(单位:ms,按需调整)
修改后的代码示例
void FooMethod() { netconn* tcpListenNetconn = NULL; netconn* connection = NULL; err_t err; // 创建监听Netconn tcpListenNetconn = netconn_new(NETCONN_TCP); if (tcpListenNetconn == NULL) { return; } // 绑定端口(开启复用后无需担心TIME_WAIT) err = netconn_bind(tcpListenNetconn, IP4_ADDR_ANY, TCP_PORT); if (err != ERR_OK) { netconn_delete(tcpListenNetconn); return; } // 开始监听 netconn_listen(tcpListenNetconn); // 等待客户端连接 err = netconn_accept(tcpListenNetconn, &connection); if (err != ERR_OK || connection == NULL) { netconn_delete(tcpListenNetconn); return; } uint8_t buffer = 0x65; while (1) { err = netconn_write(connection, &buffer, 1, NETCONN_COPY); if (err != ERR_OK) { break; } // 避免高频发送,添加适当延时 vTaskDelay(pdMS_TO_TICKS(100)); } // 处理连接资源释放 if (connection != NULL) { // 判断连接状态,避免已断开时调用close出错 if (netconn_get_state(connection) != NETCONN_NONE) { // 主动关闭读写通道 netconn_shutdown(connection, SHUT_RDWR); // 尝试关闭连接,处理操作中状态 err = netconn_close(connection); if (err == ERR_INPROGRESS) { do { vTaskDelay(pdMS_TO_TICKS(100)); err = netconn_close(connection); } while (err == ERR_INPROGRESS); } } // 循环等待删除完成 do { err = netconn_delete(connection); if (err == ERR_INPROGRESS) { vTaskDelay(pdMS_TO_TICKS(100)); } } while (err == ERR_INPROGRESS); connection = NULL; } // 释放监听Netconn if (tcpListenNetconn != NULL) { netconn_delete(tcpListenNetconn); tcpListenNetconn = NULL; } } // 重连任务:循环调用连接逻辑,添加重连间隔 void ReconnectTask(void* arg) { while (1) { FooMethod(); // 避免频繁重连,添加间隔 vTaskDelay(pdMS_TO_TICKS(2000)); } }
关键注意事项
- 必须确保所有
netconn资源都被正确释放,包括监听和连接用的实例,否则会导致内存泄漏和端口占用。 - 不要忽略
netconn_new()、netconn_bind()等操作的错误返回值,添加必要的错误处理。 - 端口复用配置
TCP_REUSE需根据实际场景调整,过度缩短TIME_WAIT可能影响TCP连接稳定性。
内容的提问来源于stack exchange,提问作者user3143155
相关产品推荐
相关产品推荐

