You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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));
    }
}
额外排查点

如果改完上述问题还是连接失败,优先检查两点:

  1. 确认嵌入式设备本地IP和目标IP192.168.225.20在同一网段,子网掩码配置正确,能正常ping通服务端,不存在IP冲突。
  2. 确认LWIP配置文件中LWIP_NETCONN、LWIP_TCP宏已经置1,TCP相关功能已开启。

内容的提问来源于stack exchange,提问作者Erwin Draconis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:39:15