做项目的时候,有时候便会和后台撕逼。有时候后台给的数据不是前端想要的。他还很固执的不想改。所以便觉得是时候包揽后台的活了。
闲话不多说,分享一下我对TFTP协议的一些理解
下载流程:
发送下载请求:
下载操作码为1,模式为octet
file_down_load_request = "!H" + str(len(fileName)) + "s" + "b" + "5s" + "b";
print(file_down_load_request)
tftp_down_request = struct.pack(file_down_load_request, 1, fileName, 0, "octet".encode("utf-8"), 0);
udpSocket.sendto(tftp_down_request, ("169.254.233.46", 69))
发送给服务端,注意这里是个元组(ip,端口)
接收数据包:
数据包的操作码为3占2个字节。整个数据包为516字节。也就是每次数据包最多返回512个字节。快编号的作用像是传递一个密钥,保证数据的是否已经下到了。当客户端下载到这个数据包时,拿到这个块编号,在发送给服务端,然后服务端再发送下一个数据包。(一想到之前有个socket控制项目,我就没想到用这个方式告诉服务端我已经接到消息,就是泪啊。看来自己功力不够)。快编号占两个字节,一个字节占八位也就16个字节,所以块编号最大为65536。那么问题来了,快编号最大为65536,那么我的下载数据最大就应该为512*65536也就是32M左右。那么超过32M怎么办呢?首先这个块编号是从1开始发的,到655537就会重置为1。
recv_data, recv_adr = udpSocket.recvfrom(516)
caozuoma, kuaibianhao = struct.unpack("!HH", recv_data[:4])
print("操作码::%s" % caozuoma) #
print("块编号::%s" % kuaibianhao)
# 块编号存放的是两位即16个字节=65536 从1开始
# 返回的是正确的数据包;2(操作码)+2(快编号)+514b的数据
if (caozuoma == 3):
if(packNum>65536):
packNum=0
packNum += 1
if (packNum == kuaibianhao):
jieShouFile.write(recv_data[4:])
downFileTotalSize+=len(recv_data[4:])
packNum = kuaibianhao;
print("文件当前下载的大小%d"%downFileTotalSize)
当收到数据包的时候,客户端还需要发送ack给服务端,告诉它,刚才发的块编号收到了,以便它发下一个块编号的数据包,ack为4个字节,两个字节操作码,2个字节快编号,以2个H占位4个字节
ackData = struct.pack("!HH", 4, packNum)
udpSocket.sendto(ackData, recv_adr)
服务端返回异常的数据包:如果收到了操作码为5的数据包这说明出现下载异常了
elif (caozuoma == 5):
print("tffp协议下载错误")
break
那么还有一个问题就是如何判定是下载完成了呢?因为一个数据包最大为516个字节,那么当我收到小于516个字节的数据包,就能判定下载成功。貌似及时最后一个数据包的大小恰巧是516,那么它还会发一个数据为0的数据包来。
if (len(recv_data) < 516):
print("tffp协议下载完成")
break
# tftp 简单文件传输协议
# 发送下载请求
import struct
import socket
fileName = "动态背景.rar".encode("gb2312");
print(len(fileName))
print(str(len(fileName)) + "s")
file_down_load_request = "!H" + str(len(fileName)) + "s" + "b" + "5s" + "b";
print(file_down_load_request)
tftp_down_request = struct.pack(file_down_load_request, 1, fileName, 0, "octet".encode("utf-8"), 0);
udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udpSocket.sendto(tftp_down_request, ("169.254.233.46", 69))
packNum = 0
jieShouFile = open("动态背景2.rar".encode("gb2312"), "wb+")
downFileTotalSize = 0;#记录下载进度
while True:
recv_data, recv_adr = udpSocket.recvfrom(516)
caozuoma, kuaibianhao = struct.unpack("!HH", recv_data[:4])
print("操作码::%s" % caozuoma) #
print("块编号::%s" % kuaibianhao)
# 块编号存放的是两位即16个字节=65536 从1开始
# 返回的是正确的数据包;2(操作码)+2(快编号)+514b的数据
if (caozuoma == 3):
if(packNum>65536):
packNum=0
packNum += 1
if (packNum == kuaibianhao):
jieShouFile.write(recv_data[4:])
downFileTotalSize+=len(recv_data[4:])
packNum = kuaibianhao;
print("文件当前下载的大小%d"%downFileTotalSize)
# 确认操作 :4个字节
ackData = struct.pack("!HH", 4, packNum)
udpSocket.sendto(ackData, recv_adr)
elif (caozuoma == 5):
print("tffp协议下载错误")
break
# 判定下载完成
if (len(recv_data) < 516):
print("tffp协议下载完成")
break
udpSocket.close()
jieShouFile.close()
那么我要像服务端上传文件该怎么弄呢,其实原理是差不多的,只不过改为我来发数据包,服务端确认,两个难点:1.服务端的端口是变的,切记传入服务端的元组地址。2.P3编解码真的恶心,判断文件读取结束的条件注意编码格式
show code
附件:上传文件
# tftp 简单文件传输协议
# 上传文件
import struct
# 定义几个变量
# 要上传到服务端的文件名
uploadToServerFileName = "HelloWorld.mkv".encode("utf-8");
# 上传进度
uploadProgress = 0;
# 起始的快编号标记
kuaiBiaoHaoFlag = 0;
# 建立UDP
import socket
udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);
# 建立读写请求
file_upload_request = "!H" + str(len(uploadToServerFileName)) + "s" + "b" + "5s" + "b";
tftp_upload_request = struct.pack(file_upload_request, 2, uploadToServerFileName, 0, "octet".encode("utf-8"), 0)
udpSocket.sendto(tftp_upload_request, ("169.254.164.219", 69))
# 要上传的文件
ready_to_upload_file = open("HelloWorld.mkv".encode("utf-8"), "rb+")
while True:
recv_data, recv_adr = udpSocket.recvfrom(516)
print(recv_data)
caozuoma, kuaibianhao = struct.unpack("!HH", recv_data[:4])
print("操作码::%s" % caozuoma) #
print("块编号::%s" % kuaibianhao)
if (caozuoma == 4):
if (kuaibianhao == kuaiBiaoHaoFlag):
print("服务端已经收到了我的数据,准备上传下一个")
# 开始上传数据,此时要像服务端发送数据包,数据大小最大为512
kuaiBiaoHaoFlag += 1;
data = ready_to_upload_file.read(512);
print(data)
if(data==("".encode("utf-8"))):
print("上传结束")
break
if (kuaibianhao == 65535):
kuaiBiaoHaoFlag = 0
kuaibianhao = 0
# 数据包
data_request = "!H" + "H" + str(len(data)) + "s"
shujuBao = struct.pack(data_request, 3, kuaiBiaoHaoFlag, data)
udpSocket.sendto(shujuBao, recv_adr)
else:
print("服务端没收到我上传的数据")
break
udpSocket.close()
ready_to_upload_file.close()