TFP下载抓包分析:
test.txt octet blksize 1024 timeout 5 tsize 0 0浍喃
00 01 74 65 73 74 2E 74 78 74 00 6F 63 74 65 74
00 62 6C 6B 73 69 7A 65 00 31 30 32 34 00 74 69
6D 65 6F 75 74 00 35 00 74 73 69 7A 65 00 30 00
30 E4 AB 02 10 E0 AB 02
服务器回应:
blksize 1024 timeout 5 tsize 10 TTP/1.1
00 06 62 6C 6B 73 69 7A 65 00 31 30 32 34 00 74
69 6D 65 6F 75 74 00 35 00 74 73 69 7A 65 00 31
30 00 54 54 50 2F 31 2E 31 0D
客户端发送:
00 04 00 00 00 00 00 00 00 00 00 00
服务端发送文件内容:
00 03 00 01 31 31 32 32 33 31 31 32 32 33 00 00
00 00 00 00 00 00
客户端回应:(结束任务)
00 04 00 01 00 00 00 00 00 00 00 00
逐包完整拆解 + 问题定位(你这条抓包有两处明显异常)
前置 TFTP 操作码定义
0x01 = RRQ(下载请求)0x03 = DATA(文件数据块)0x04 = ACK(确认块)0x06 = OACK(扩展协商应答)
第 1 包:客户端 → MCU 69 端口 RRQ 下载请求
十六进制开头 00 01内容结构(00 作为字符串分隔符):
plaintext
test.txt\0
octet\0
blksize\01024\0
timeout\05\0
tsize\00\0
含义:
- 客户端要下载
test.txt,二进制 octet 模式; - 协商参数:单块 1024 字节、超时 5 秒;
tsize 0:下载场景客户端不知道文件大小,填 0;尾部乱码30 E4 AB 02 10 E0 AB 02是包末尾多余垃圾字节,无关协议。
产生扩展参数原因:你代码里设置了
transfer.BlockSize = 1024,TFTP.Net 1.3 只要指定块大小就自动带上 blksize/timeout/tsize,无法关闭。
第 2 包:MCU 随机端口 → 客户端 OACK(00 06)协商应答
plaintext
blksize\01024\0
timeout\05\0
tsize\010\0
MCU 告知客户端:同意使用 1024 块、5 秒超时,并且我本地这个文件总大小是10 字节。
严重异常点:末尾多出 TTP/1.1\r 垃圾文本
正常 OACK 包只包含三组 key-value,你报文尾部多了 54 54 50 2F 31 2E 31 0D = TTP/1.1\r原因:单片机发送 UDP 数据包时缓冲区未清空,残留了 HTTP 协议字符串,污染 TFTP 包。后果:部分严格 TFTP 客户端会解析失败、直接超时断开,必须修复 MCU 发包缓冲区逻辑。
第 3 包:客户端 → MCU ACK 0 号块(00 04 00 00)
收到 OACK 之后,客户端回复 ACK 0,表示认可协商参数,等待服务端下发文件数据块。
第 4 包:MCU → 客户端 DATA 块 1(00 03 00 01)
00 03= DATA 包00 01= 块编号 1后面31 31 32 32 33...就是文件原始二进制内容,总长 10 字节,不足 1024,代表文件末尾。
第 5 包:客户端 → MCU ACK 块 1(00 04 00 01)
客户端确认完整收到第 1 块数据;文件只有 10 字节,仅此一块,传输直接结束。
整体完整交互流程梳理
- PC 发 RRQ:请求下载 test.txt,协商 1024 块、5s 超时
- MCU 回 OACK:确认参数,告知文件大小 10 字节(包尾带 HTTP 垃圾字符 bug)
- PC 回 ACK0:准备接收数据
- MCU 发送 DATA#1:10 字节文件内容
- PC 回 ACK1:传输完成
两个必须修复的问题
1、单片机 UDP 发送缓冲区脏数据(OACK 尾部多出 TTP/1.1)
MCU 发送 OACK 前,必须清空发送缓存数组,不要复用同时处理 HTTP 的缓冲区;复用缓冲区且未清零,上次 HTTP 数据残留混入 TFTP UDP 包,会导致兼容性故障。
2、客户端强制产生扩展协商的规避方案
只要写了BlockSize=1024就会带 OACK:
方案 A(推荐,无扩展、无 OACK 包)
删除 transfer.BlockSize = 1024,使用协议默认 512 字节块;此时 RRQ 仅包含 test.txt\0 octet\0,无 blksize/tsize,MCU 不用处理 06 操作码。
TransferMode = Octet
' 删掉 BlockSize 赋值,不要手动指定块大小
方案 B(必须 1024 块,只能修改 MCU 代码兼容 OACK)
MCU 解析 RRQ 逻辑修改:
- 先读取文件名(读到第一个 0x00)
- 再读取 mode 字符串(读到第二个 0x00)
- 循环向后读取
key\0value\0键值对,全部丢弃,直到整个 UDP 包读完 - 增加
0x06 OACK操作码分支,能正常组装并回复 OACK 包
对比 HTTP 的优势再次凸显
HTTP 不存在 UDP 缓冲区复用污染、OACK 协商、端口切换、块确认这套复杂流程;TCP 流式收发,缓冲区隔离简单,不会出现协议数据互相串扰的 bug,开发调试成本低非常多。