应一个朋友要求,使用python制作了一个图形化烧写鸿蒙OS的工具,发现目前网上开源的tftp代码存在疏漏,无法传输大文件,本文介绍了原因和修复方法。
一、鸿蒙系统烧写原理之前参加过鸿蒙系统的推广,获得了一块基于Hi3516DV300的摄像头,也曾尝试自己编译鸿蒙系统和重新烧写。在开发环境编译系统后,得到三个文件:
Rootfs.img

Userfs.img
OHOS_Image.bin
将其导入板卡后,再进行一系列设置完成升级。
其关键步骤,是通过网络使用tftp将文件导入mmc。
华为官方推出的烧写工具hitool使用起来比较复杂,大部分参数需要手工计算填写,烧写完成后还需要手工更改启动参数,不少朋友表示不太适应。故应朋友之邀,做了一个简化的烧写工具。
二、工具制作采用python编写,主要调用了tkinter、serial、socket等库实现了工具界面和功能的开发。
图一
因为python没有直接的tftp库,因此tftp采用socket开发,借用了网上的开源代码。
在最初开发时,发现rooft.img和userfs.img文件传输都正常,但传输OHOS_Image.bin无法通过CRC验证。找了多个出处,借鉴了不同开源代码,均存在同类问题,最终大致确定原因python下的开源tftp代码不支持大小超过33M的文件,否则会出现错误。
但事实上,使用官方工具hitool可以正常传输一个镜像,显然不是板卡问题或tftp协议本身的限制,应该还是代码引入的问题。
三、问题分析在《TCP/IP详解卷1》章节15中介绍了TFTP,原理非常简单:数据以定长512字节传输,收到对方的应答数据包后发送下一数据包,直至数据大小小于512字节,认为传输结束。在这个过程中,如果发送方没有收到应答,会超时重传,通信的双方都是数据的发出者与接收者,一方传输数据接收应答,另一方发出应答接收数据。
在RFC1350的Figure 5-2(https://datatracker.ietf.org/doc/rfc1350/)中,定义了数据传输格式:
图二
当Opcode为3时,表示数据传输,具有块号(Block#)和数据字段(Data)。数据包上的块号从一个开始,每一个新的数据块增加一个。因为块号只有2 bytes,所以只能支持0~65535的数字,再乘以Data的固定长度512字节,大约就是限制33M。这就是网上所能找到代码无法传输大文件的根本原因!
找到原因以后,解决方法也很简单,在RFC1350中直接给出了解决方案:使用重复的Block号(“The block numbers on data packets begin with one and increase by one for each new block of data. This restriction allows the program to use a single number to discriminate between new packets and duplicates.”)在程序中也只要加上两行:
图三
完美解决该问题。
四、心得在出现问题之前,并没有想过公开代码的网友们是否已经充分验证了代码的有效性,直接拿来引用,发现问题后,还一度认为是网线或是操作步骤的导致,没想过几万人使用也许都没有试过大文件传输,或是发现了问题也没有说;使用暴力验证可以传输的最大文件,以为定位了问题,但不读RFC文档,就没有考虑过这个33M限制是哪来的,自然也想不到解决办法。
总的说来:
1、代码加注释是好习惯,没有注释的代码读起来太痛苦。
2、一本专业的参考书应该随时放在手边,不时翻翻,总有新的体悟。
3、不能迷信已有的代码,应该自己动手,就算是经过其它人验证了的成熟模块,在使用前,也可以设置各种边界条件,多加验证,做到心中有数,不然代码重用时难免会碰到意料之外的情况。