Python3 积累一些简单文件传输协议(TFTP)的知识点


        做项目的时候,有时候便会和后台撕逼。有时候后台给的数据不是前端想要的。他还很固执的不想改。所以便觉得是时候包揽后台的活了。

闲话不多说,分享一下我对TFTP协议的一些理解

下载流程:

       Python3 积累一些简单文件传输协议(TFTP)的知识点_第1张图片

发送下载请求:


下载操作码为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);

操作码为2个字节,H像是占位符一样占两个字节,模式octet占五个字节。注意编解码,这是我觉得p2与p3最让我蛋疼的地方

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个字节

Python3 积累一些简单文件传输协议(TFTP)的知识点_第2张图片

        ackData = struct.pack("!HH", 4, packNum)
        udpSocket.sendto(ackData, recv_adr)
服务端返回异常的数据包:如果收到了操作码为5的数据包这说明出现下载异常了

Python3 积累一些简单文件传输协议(TFTP)的知识点_第3张图片


    elif (caozuoma == 5):
        print("tffp协议下载错误")
        break

那么还有一个问题就是如何判定是下载完成了呢?因为一个数据包最大为516个字节,那么当我收到小于516个字节的数据包,就能判定下载成功。貌似及时最后一个数据包的大小恰巧是516,那么它还会发一个数据为0的数据包来。

    if (len(recv_data) < 516):
        print("tffp协议下载完成")
        break

附件:下载完整代码,python3注意编解码啊,真的蛋疼

# 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()






你可能感兴趣的:(Python)