随着互联网发展,文件传输效率越来越快,相应的传输协议也越来越复杂。早年有很多文件传输协议如今已经很少再用,所谓老兵不死,只是慢慢凋零。这些协议尽管现在使用不多,但它们的设计思想依然值得我们好好研究和掌握。
例如FTP以及它的UDP版本TFTP,它们实现文件传输的协议设计思想依然非常值得研究,它对我们设计新协议依然很有启发性。本节开始,我们研究TFTP协议的原理以及相关代码实现。
TFTP原名叫Trivial File Transport Protocol。对互联网早期存有记忆的同学对FTP协议一定非常了解,当时局域网乃至整个网络上很多大文件,例如电影的传输依靠的就是该协议。FTP协议运行在TCP协议之上,它的内容很复杂,除了文件传输外,它还支持很多文件相关操作,例如远程实现文件建立,删除等。TFTP是FTP协议的简化版,它运行在UDP协议上,同时简化了很多FTP操作,只支持文件的传输功能。
TFTP协议是基于服务器和客户端之间的传输协议。一开始客户端向服务器发出连接请求,服务器应答后两者连接建立。然后客户端向服务器发出文件传输请求,服务器将客户端需要的文件分割成多个小块,依次传递给客户端,客户端每收到一个小块后向服务器发出应答,收到应答后服务器再发送下一个小块。当所有文件块传输完毕后,两者连接断开。
TFTP服务器程序通常在端口69监听客户端请求。值得注意的是,当服务器与客户端进行数据块传输时,服务器会使用一个随机端口而不是用于监听请求的69端口,这是为了服务器能同时相应多个客户端的连接。服务器与不同客户端使用不同端口进行数据通信,这样就保证不同客户端所需要的数据块不会发生混淆。
服务器与客户端在发送文件数据时,会按照一种名为”锁定步骤“的方式进行数据传输。也就是服务器向客户端发送一个数据块,再接收到客户端发回的应答数据包前什么都不做,直到收到客户端确定数据块已经收到的应答后,它才发送下一个数据块,这种方式使得数据传输效率不高,但确保数据传输流程足够简单,同时能保证传输出错时,数据重传很方便,同时客户端也不用考虑数据块不按次序抵达时,如何将数据块进行正确组装。
TFTP协议的简单附带的代价是效率不高。由于它走的是UDP协议,因此一次发送数据块不能超过512字节,这也是服务器必须把文件切成小块反复传输的原因。还有一点值得注意的是TFTP协议没有任何安全措施,它不需要注册或登录,任何客户端都可以连接然后下载文件。
我们看看TFTP的一个运行实例。首先我在虚拟机中的win10系统安装应用程序tftpd64作为TFTP协议的服务器,具体情况如下:
上图显示出服务器当前路径是C:\Program Files\Tftpd64,10.211.55.3是服务器所在电脑的IP,它会在端口69上监听客户端的请求。由于mac操作系统自带tftp客户端,因此我们可以直接在控制台上输入tftp命令后就可以运行客户端。运行客户端后,我使用命令connect 10.211.55.3来连接上面显示的tftp服务器:
在tftp服务器程序对应的路径下,有一个文件叫EUPL-EN.pdf:
然后我在客户端执行命令 get EUPL-EN.pdf就可以将文件下载到本地:
我在服务器端运行wireshak抓包,所得结果如下:
图中显示两种数据包,分别是Acknowledgement和Data block,其中前者是客户端收到数据块后对服务器的应答,后者是服务器向客户端发送的数据块。数据包的具体格式我们会在后面进行详细分析。
现在我们看看协议的交互细节。tftp主要分为三步,首先是连接,然后是数据传输,最后是连接中断。所有这些步骤都通过发送相关数据包完成。最开始由客户端发送一个数据读取或写入请求,这个请求发出的同时连接自动建立,在这个过程中双方会协议要传输什么格式的文件。TFTP协议支持两个格式文件的传输,分别是ASCII文本,另一种树二进制数据,FTP协议支持的文本格式比TFTP要复杂得多。
如果客户端请求的文件存在,服务器会直接将第一个数据块发送给客户端。如果是客户端想上传文件,服务器会发送一个ACK数据包表示确认。在这个过程中如果出现错误,其中一方就向另一方发送错误信息数据包,然后文件传输终止。由于使用UDP作为底层协议,因此一次数据发送最大不超过512字节。因此为了保证数据顺序正确性,每个数据包必须对应相应编号,编号根据数据块的顺序从1开始。
由于每次数据块最大是512字节,只要文件传输没有结束,那么一次数据块就是512字节,如果有数据包中数据少于512字节,那意味着这是文件最后一个数据包,最后一个数据块发送完后,连接自动中断。我们通过一个具体实例来掌握数据发送流程,假设客户端想从服务器读取一个1200字节的文件,以下是相关步骤:
1,客户端发送一个数据包给服务器,其中包含了要读取的文件名。
2,服务器发回第一个512字节数据块,并对其标号为1.
3,客户端返回服务器一个标号为1的确认数据包
4,服务器发送标号为2包含512字节的数据块
5,客户端收到2号数据块后发生确认数据包
6,服务器发送标号为3的包含176字节的数据块
7,客户端收到后回发标号为3的确认数据包
8,服务器收到确认数据包后,确认文件发送完毕
上面流程可以使用下图表示:
我们再看看客户端上传文件的流程:
1,客户端发送一个写请求数据包,里面包含了要写的文件名称
2,服务器发送确认数据包,在数据包中它使用编号0
3,客户端发送一个含有512字节,编号为1的数据包
4,服务器返回编号为1的确认数据包
5,客户端发送编号为2,包含512字节的数据包
6,服务器返回编号为2的确认数据包
7,客户端发送编号为3,包含176字节的数据包,等待服务器返回确认数据包。
8,服务器接受3号数据包后,返回确认数据包,由于该数据包数据少于512字节,服务器知道这是最后一个数据包。
9,客户端收到3号确认数据包后,知道文件传输完毕,中断连接。
上面流程可以使用下图表示:
TFTP协议后来又经过一次扩展,增加一些控制命令。如果客户端或服务器想使用扩展命令时,它必须向对方确认是否也能支持相应命令。它会发送一个数据包,里面包含扩展命令对应的数值列表,对方也会返回一个列表,把它支持的扩展命令对应的数值放在列表中,不支持的则不在列表里。TFTP协议一个特点是,它不允许任何一方连续发送2个数据包,必须是一来一回。具体的扩展功能在协议实现时我们再详细研究。
下一节我们将探讨数据包的具体格式,为代码实现做进一步准备。
新书上架,请诸位朋友多多支持:请关注公众号,让我们共同学习进步