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

STM32H767基于LwIP的TCP连接三次握手后被RST拒绝的问题排查

STM32H767 LwIP TCP服务器连接后立即RST问题排查与数据包捕获指导

问题现象

基于STM32H767的LwIP TCP服务器实现中,三次握手可正常完成,但握手结束后开发板立即发送[RST, ACK]包关闭连接;recv_callback未触发(LED无闪烁),但tcp_echoserver_accept可正常执行。抓包信息如下:

6021    2919.149663 192.168.33.101  192.168.33.1    TCP 66  65222 → 8 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM
6022    2919.150751 192.168.33.1    192.168.33.101  TCP 60  8 → 65222 [SYN, ACK] Seq=0 Ack=1 Win=2144 Len=0 MSS=536
6023    2919.150997 192.168.33.101  192.168.33.1    TCP 54  65222 → 8 [ACK] Seq=1 Ack=1 Win=65392 Len=0
6024    2919.151743 192.168.33.1    192.168.33.101  TCP 60  8 → 65222 [RST, ACK] Seq=1 Ack=1 Win=24584 Len=0

问题排查与代码修正

1. 核心错误点分析

错误1:未初始化全局指针导致未定义行为

tcp_echoserver_accept中直接操作全局变量tcp_newpcb,但该变量未被赋值为参数传入的newpcb,直接访问tcp_newpcb->state会破坏内存,触发LwIP内部异常,最终发送RST包。

错误2:accept回调未返回正确值

LwIP要求tcp_accept回调必须返回err_t类型值,未返回会被LwIP判定为连接处理失败,主动关闭连接。

错误3:多余的状态设置

手动设置state = ESTABLISHED完全多余,LwIP在三次握手完成后已经自动将连接状态设为ESTABLISHED。

2. 修正后的代码

修正tcp_echoserver_accept函数

err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    tcp_newpcb = newpcb; // 给全局指针赋值,绑定当前连接的pcb
    tcp_arg(newpcb, newpcb); // 将pcb本身作为回调参数传递
    tcp_recv(newpcb, recv_callback); // 注册接收回调

    return ERR_OK; // 必须返回ERR_OK,告知LwIP连接处理成功
}

修正recv_callback函数(移除阻塞逻辑)

原代码中的无限循环会阻塞整个LwIP协议栈(裸机场景下LwIP为单线程执行),修正为正常处理数据包的逻辑:

err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p == NULL) {
        // 客户端主动关闭连接,释放pcb资源
        tcp_close(tpcb);
        return ERR_OK;
    }

    // 示例:回显收到的数据
    tcp_write(tpcb, p->payload, p->len, TCP_WRITE_FLAG_COPY);
    tcp_output(tpcb);

    // 释放pbuf内存,避免内存泄漏
    pbuf_free(p);

    // LED闪烁验证回调触发
    HAL_GPIO_TogglePin(GPIOB, LD1_Pin);
    return ERR_OK;
}

开发板上捕获与存储TCP数据包的方法

方法1:LwIP调试输出

  • 在lwipopts.h中开启调试宏:
    #define LWIP_DEBUG 1
    #define TCP_DEBUG LWIP_DBG_ON
    #define ETHARP_DEBUG LWIP_DBG_ON
    
  • 重定向printf到开发板的USART串口,通过串口终端即可查看LwIP内部的数据包接收、处理日志,适合调试阶段使用。

方法2:驱动层缓冲区存储

在以太网驱动的ethernetif_input函数中,将收到的数据包存储到SRAM或外部Flash:

  1. 定义环形缓冲区:
    #define PACKET_BUF_SIZE 8192 // 8KB缓冲区
    uint8_t packet_buf[PACKET_BUF_SIZE];
    uint32_t packet_write_ptr = 0;
    
  2. 修改ethernetif_input捕获数据包:
    err_t ethernetif_input(struct netif *netif)
    {
      struct pbuf *p;
      p = low_level_input(netif);
      if (p != NULL) {
          // 存储数据包到缓冲区(避免溢出)
          uint32_t len = p->tot_len;
          if ((packet_write_ptr + len) < PACKET_BUF_SIZE) {
              pbuf_copy_partial(p, &packet_buf[packet_write_ptr], len, 0);
              packet_write_ptr += len;
          }
          // 正常交付LwIP协议栈处理
          if (netif->input(p, netif) != ERR_OK) {
            pbuf_free(p);
            p = NULL;
          }
      }
      return ERR_OK;
    }
    
  3. 后续可通过串口将缓冲区数据导出到PC,或写入SD卡的PCAP格式文件,直接用Wireshark分析。

方法3:硬件旁路抓包

将开发板以太网接口的RX/TX引脚通过网络分线器连接到PC网卡,直接用Wireshark抓包,该方法不影响开发板性能,适合高流量场景调试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:15:01