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:
- 定义环形缓冲区:
#define PACKET_BUF_SIZE 8192 // 8KB缓冲区 uint8_t packet_buf[PACKET_BUF_SIZE]; uint32_t packet_write_ptr = 0; - 修改
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; } - 后续可通过串口将缓冲区数据导出到PC,或写入SD卡的PCAP格式文件,直接用Wireshark分析。
方法3:硬件旁路抓包
将开发板以太网接口的RX/TX引脚通过网络分线器连接到PC网卡,直接用Wireshark抓包,该方法不影响开发板性能,适合高流量场景调试。
内容的提问来源于stack exchange,提问作者confusedfresher
相关产品推荐
相关产品推荐

