FreeRTOS下LWIP netconn TCP客户端报err13连接中止问题咨询
你的代码存在多处明确问题,其中几处会直接触发netconn_connect返回错误码13(ERR_ABRT,连接中止),具体问题和修复方案如下:
直接导致连接失败的核心问题
- TCP线程栈分配过小,触发栈溢出
你在创建tcpinit_thread线程时仅分配了1024字节栈空间,这个大小完全不足以支撑LWIP netconn接口调用+printf/sprintf等标准库函数的栈开销。这类函数单函数栈占用通常可达300~600字节,加上LWIP内核协议栈处理的栈消耗,1024字节栈会直接溢出,篡改内存中netconn连接结构、TCP控制块数据,最终导致连接流程被内核强制中止返回13错误。 - 未等待网卡就绪就发起连接
代码中没有任何判断本地网卡状态、本地IP有效性的逻辑,只要线程启动就直接发起connect。如果此时底层网卡还未完成链路up、DHCP未获取到有效IP/静态IP未绑定到网卡,LWIP内核没有可用的本地IP封装TCP SYN包,会直接中止连接返回错误。 - 错误处理逻辑不规范,无重连机制
连接失败时你先调用netconn_close再调用netconn_delete,但connect返回错误时内核可能已经释放了连接对应的部分资源,强行调用close会触发非法内存访问,反过来破坏内核状态。同时代码连接失败后直接退出线程,没有重试逻辑,首次连接失败后就不会再发起连接尝试。
连接成功后会触发崩溃的隐患问题
这些问题虽然不影响连接建立,但一旦连通会直接导致程序跑飞:
- 接收数据时未做长度校验:
strncpy(msgc, buf->p->payload, buf->p->len)没有判断接收长度是否超过msgc的100字节缓冲区上限,一旦服务端发送超过99字节的数据就会触发缓冲区溢出。 - 未手动补字符串结束符:
strncpy只有在源数据长度小于拷贝长度时才会自动补\0,如果接收数据长度等于拷贝长度,后续sprintf读取msgc时会越界访问野内存。 - 发送回包时用
sprintf没有做长度限制,同样存在smsgc缓冲区溢出风险。
修复参考代码
线程初始化修正
把栈大小调整到至少2048字节(内存紧张的场景也不要低于1536字节,可根据实际平台压测调整):
void tcpclient_init (void) { sys_thread_new("tcpinit_thread", tcpinit_thread, NULL, 2048, DEFAULT_THREAD_PRIO); }
核心连接线程修正
增加网卡就绪判断、失败重连逻辑,修复缓冲区溢出问题:
static struct netconn *conn; static struct netbuf *buf; static ip_addr_t *addr, dest_addr; static unsigned short port, dest_port; char msgc[100]; char smsgc[200]; void tcpinit_thread() { err_t connect_error; // 等待网卡链路up、本地IP有效后再发起连接 while (netif_is_link_up(netif_default) == 0 || ip4_addr_isany_val(*netif_ip4_addr(netif_default))) { vTaskDelay(pdMS_TO_TICKS(100)); } while (1) { conn = netconn_new(NETCONN_TCP); if (conn == NULL) { vTaskDelay(pdMS_TO_TICKS(500)); continue; } // 设置连接、收发超时为5秒,避免永久阻塞 netconn_set_sendtimeout(conn, pdMS_TO_TICKS(5000)); netconn_set_recvtimeout(conn, pdMS_TO_TICKS(5000)); IP_ADDR4(&dest_addr, 192, 168, 225, 20); dest_port = 5656; connect_error = netconn_connect(conn, &dest_addr, dest_port); if (connect_error == ERR_OK) { printf("TCP client connected on port 5656 \r\n"); while (1) { if (netconn_recv(conn, &buf) == ERR_OK) { addr = netbuf_fromaddr(buf); port = netbuf_fromport(buf); do { memset(msgc, 0, sizeof(msgc)); // 限制最大拷贝长度,避免缓冲区溢出 uint16_t copy_len = buf->p->len > (sizeof(msgc)-1) ? (sizeof(msgc)-1) : buf->p->len; memcpy(msgc, buf->p->payload, copy_len); msgc[copy_len] = '\0'; // 手动补字符串结束符 // 用snprintf限制写入长度,避免溢出 snprintf(smsgc, sizeof(smsgc), "\"%s\" was sent by the Server\n", msgc); printf("data received from server \r\n"); if (netconn_write(conn, smsgc, strlen(smsgc), NETCONN_COPY) != ERR_OK) { netbuf_delete(buf); break; } } while (netbuf_next(buf) > 0); netbuf_delete(buf); } else { // 接收异常,断开连接重连 break; } } } // 连接异常时直接删除conn即可,不需要额外调用close避免非法访问 netconn_delete(conn); conn = NULL; printf("connect failed, retry after 1s \r\n"); vTaskDelay(pdMS_TO_TICKS(1000)); } }
额外排查点
如果改完上述问题还是连接失败,优先检查两点:
- 确认嵌入式设备本地IP和目标IP192.168.225.20在同一网段,子网掩码配置正确,能正常ping通服务端,不存在IP冲突。
- 确认LWIP配置文件中
LWIP_NETCONN、LWIP_TCP宏已经置1,TCP相关功能已开启。
内容的提问来源于stack exchange,提问作者Erwin Draconis
相关产品推荐
相关产品推荐

