最近研究了一下,TFTP协议,尽管这种协议目前用的不多,但是却很适合小文件的传递。
简单归纳一下如下:
TFTP(Trivial File Transfer Protocol,简单文件传输协议)
特点:
简单
占用资源小
适合传递小文件
适合在局域网进行传递
端口号为69
基于UDP实现
下载过程:
TFTP服务器默认监听69号端口,当客户端发送“下载”请求(即读请求)时,需要向服务器的69端口发送,服务器若批准此请求,则使用一个新的、临时的 端口进行数据传输。
当服务器找到需要下载的文件后,会立刻打开文件,把文件中的数据通过TFTP协议发送给客户端,如果文件的总大小较大(比如3M),那么服务器分多次发送,每次会从文件中读取512个字节的数据发送过来。因为发送的次数有可能会很多,所以为了让客户端对接收到的数据进行排序,所以在服务器发送那512个字节数据的时候,会多发2个字节的数据,用来存放序号,并且放在512个字节数据的前面,序号是从1开始的。
因为需要从服务器上下载文件时,文件可能不存在,那么此时服务器就会发送一个错误的信息过来,为了区分服务发送的是文件内容还是错误的提示信息,所以又用了2个字节 来表示这个数据包的功能(称为操作码),并且在序号的前面。
操作码 功能
1 读请求,即下载
2 写请求,即上传
3 表示数据包,即DATA
4 确认码,即ACK
5 错误
因为udp的数据包不安全,即发送方发送是否成功不能确定,所以TFTP协议中规定,为了让服务器知道客户端已经接收到了刚刚发送的那个数据包,所以当客户端接收到一个数据包的时候需要向服务器进行发送确认信息,即发送收到了,这样的包成为ACK(应答包)。
为了标记数据已经发送完毕,所以规定,当客户端接收到的数据小于516(2字节操作码+2个字节的序号+512字节数据)时,就意味着服务器发送完毕了。
#coding=utf-8
'''
write by zxy987872674
'''
import socket
import struct
import sys
if len(sys.argv) != 3:
print('----tips-----')
print('请按照以下格式输入:')
print('python 需运行的文件名 ip 需下载的文件名')
exit()
else:
ip = sys.argv[1]
downloadFile = sys.argv[2]
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sendAddr = (ip,69) #电脑系统为mac,此处是windows虚拟机的地址
packStr = '!H' + str(len(downloadFile)) + 'sb5sb'
sendData = struct.pack(packStr,1,downloadFile,0,'octet',0)
#发送请求
s.sendto(sendData,sendAddr)
newFile = 0
num = 0
while True:
#服务器接收到请求后,返回数据,客户端接收
recvData = s.recvfrom(1024)
recvContent = recvData[0]
#接收到的ip和port,提取出来便于返回确认信息
recvAddr = recvData[1]
#解析接收到的数据内容
recvNum = struct.unpack('!HH',recvContent[:4])
#操作码
actionCode = recvNum[0]
if actionCode == 3:
#块编号
blockNum = recvNum[1]
if blockNum == 1 and newFile == 0:
newFile = open(downloadFile,'w')
print('uuuuuuuuuu')
#返回确认信息给服务器
confirmData = struct.pack('!HH',4,blockNum)
s.sendto(confirmData,recvAddr)
#保存接收到的数据,保存到同名文件中即可
if num+1 == blockNum:
newFile.write(recvContent[4:])
num += 1
if num == 65535:
num=0
if len(recvContent)<516:
newFile.close()
break
if actionCode == 5:
print('------error!------')
break
s.close()