# -*-coding:utf-8 -*- ''' tftp,操作码讲解: 读写请求: 操作码(2Bytes),文件名(nBytest),0(1Bytes),"octet"(5Bytes),0(1Bytes) 数据包:操作码(2Bytes),块编码(2Bytes),数据(512Bytes) ACK: 操作码(2Bytes),快编码(2Bytes) ERROP: 操作码(2Bytes),差错码(2Bytes),差错信息(nBytes),0(1Bytes) 1:读请求,即下载 2:写请求,即上传 3:表示数据包,即DATA 4:确认吗,即ACK 5:错误 ''' from socket import * import struct import os def main(): # 获取要下载的文档 DownloadFileName = raw_input("请输入要下载的文档:") # 创建UDP套接字 UdpSocket = socket(AF_INET, SOCK_DGRAM) # 协议规范,转换为网络大端格式 RequestFileData = struct.pack("!H%dsb5sb" % len(DownloadFileName), 1, DownloadFileName, 0, 'octet', 0) # 目标地址 SendAddr = ("192.168.5.1", 69) # UDP发送 UdpSocket.sendto(RequestFileData, SendAddr) # 创建文件,写入文件 f = open(DownloadFileName, 'w') # 定义一个变量,用于记录块编码 num = 0 # 定义一个变量,用于判断TFTP没有找到该文件时,删除空文件,默认在TFTP上有该文件 File = True while True: # 用于接收tftp返回值 ResponseData = UdpSocket.recvfrom(1024) # tffp返回值是以元组形式返回,且前面是数据,最后是服务端IP及PORT,且需要记录该IP及Port,用于反馈ACK RecvData, ServerInfo = ResponseData # 操作码,以元组形式返回 OpNum = struct.unpack("!H", RecvData[:2]) # 块编码,以元组形式返回 PacketNum = struct.unpack("!H", RecvData[2:4]) # 判断收到的操作码,如果为3,即为读模式 if OpNum[0] == 3: num = num + 1 # 当下载的数据过大,2字节已经不够表示块编码时,会从0开始重新计算 if num == 65536: num = 0 # 判断记录的块编码是否等于传输的块编码,防止数据混乱 if num == PacketNum[0]: f.write(RecvData[4:]) num = PacketNum[0] # 整理ACK的数据包,反馈给TFTP AckData = struct.pack("!HH", 4, PacketNum[0]) UdpSocket.sendto(AckData, ServerInfo) # 返回的操作码等于5,报错 elif OpNum == 5: print("没有找到该文件") File = False # 当TFTP返回值小于516时,表明数据已经传输到最后一次 if len(RecvData) < 516: break # 判断文件是关闭还删除 if File == True: # 默认在TFTP上找到该文件,状态一直未True, f.close() else: # 如果没有找到该文件,操作码会报错,返回5,由上面将File该为False,以下代码执行删除文件 os.unlink(DownloadFileName) if __name__ == "__main__": main()
Python之TFTP客户端下载
最新推荐文章于 2024-08-24 09:27:50 发布