Python实现TFTP上传和下载
1. TFTP协议介绍
- TFTP(Trivial File Transfer Protocol,简单⽂件传输协议)是TCP/IP协议族中的⼀个⽤来在客户端与服务器之间进⾏简单⽂件传输的协议
- 特点:
- 简单
- 占⽤资源⼩
- 适合传递⼩⽂件
- 适合在局域⽹进⾏传递
- 端⼝号为69
- 基于UDP实现
2. TFTP客户端服务器交互过程
- TFTP服务器默认监听69号端⼝,当客户端发送“下载”请求(即读请求)时,需要向服务器的69端⼝发送,服务器若批准此请求,则使⽤⼀个新的、临时的 端⼝进⾏数据传输。
- TFTP客户端服务器交互过程,图片来自百度图片
![图片来自百度图片](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1560265166562&di=94d57599c40141a42a1f0b117fd19647&imgtype=0&src=http%3A%2F%2Faliyunzixunbucket.oss-cn-beijing.aliyuncs.com%2Fjpg%2F8dd85448aedac034d6ae18b37a8b4e50.jpg%3Fx-oss-process%3Dimage%2Fresize%2Cp_100%2Fauto-orient%2C1%2Fquality%2Cq_90%2Fformat%2Cjpg%2Fwatermark%2Cimage_eXVuY2VzaGk%3D%2Ct_100)
- TFTP数据包格式,图片来自百度图片
![](https://i-blog.csdnimg.cn/blog_migrate/67ec7525c599ce8f3bb6d08596477644.png)
TFTP文件上传下载过程分析
1、下载过程
- 第一步:客户端给服务器发送下载请求,数据格式为(操作码1+文件名+0+模式+0)。
- 第二步:服务器接收到请求之后,回复客户端消息,数据格式为元组类型。(操作码3+块编码0+数据, (IP号, 端口号))。
- 第三步:客户端每接受一次数据,都要回复服务器一次ACK信号。
- 第四步:直到客户端接收到的数据小于516个字节,才说明服务器发送完毕!
2、上传过程
- 第一步:客户端给服务器发送上传请求,数据格式为(操作码2+文件名+0+模式+0)。
- 第二步:服务器接收到请求之后,回复客户端ACK消息,数据格式为元组类型。(操作码4+块编码0, (IP号, 端口号))。
- 第三步:客户端每发送一次数据,服务器都要回复一次ACK信号。
Python实现TFTP客户端上传下载代码
"""
tftp下载文件
地址:("127.0.0.1", 69)
"""
import struct
import socket
import sys
ip = ""
filename = ""
s = None
def handle_argv():
"""处理输入参数"""
global ip
global filename
if len(sys.argv) != 3:
print("-"*30)
print("请输入正确格式:python xxx.py ip filename")
print("例如:python xxx.py 127.0.0.1, test.txt")
print("-"*30)
else:
ip = sys.argv[1]
filename = sys.argv[2]
def recv_data():
"""接收数据"""
data_length = 0
code = 0
block_number = 0
current_number = 0
while True:
data, addr = s.recvfrom(1024)
code, current_number = struct.unpack("!HH", data[:4])
data_length = len(data)
if code == 3:
if current_number == 1:
f = open(filename, 'ab')
if block_number+1 == current_number:
f.write(data[4:])
block_number += 1
print("第{}次接收到数据!".format(current_number))
reply_info = struct.pack('!HH', 4, block_number)
s.sendto(reply_info, addr)
if data_length < 516:
f.close()
print("下载完成!")
break
else:
print("error info {}:", data.decode())
break
def main():
global s
handle_argv()
read_args = struct.pack("!H{}sb5sb".format(len(filename)), 1, filename.encode(), 0, 'octet'.encode(), 0)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 6666))
s.sendto(read_args, (ip, 69))
recv_data()
s.close()
if __name__ == "__main__":
main()
"""
tftp上传文件
地址:('127.0.01', 69)
"""
import socket
import struct
import sys
ip = ""
filename = ""
s = None
def handle_argv():
"""处理输入参数"""
global ip
global filename
if len(sys.argv) != 3:
print("-"*30)
print("请输入正确格式:python xxx.py ip filename")
print("例如:python xxx.py 127.0.0.1, test.txt")
print("-"*30)
else:
ip = sys.argv[1]
filename = sys.argv[2]
def upload_file():
"""上传文件"""
code = 0
block_number = 0
current_number = 1
while True:
data, addr = s.recvfrom(1024)
code, block_number = struct.unpack("!HH", data[:4])
if code == 4:
if block_number == 0:
f = open(filename, 'rb')
if block_number+1 == current_number:
file_data = f.read(512)
send_data = struct.pack('!HH', 3, current_number) + file_data
s.sendto(send_data, addr)
print("第{}次传输数据!".format(current_number))
block_number += 1
current_number += 1
if len(send_data) < 516:
f.close()
print("文件上传完成!")
break
elif code == 5:
f.close()
print("上传文件失败!error info: {}".format(data[4:].decode()))
break
def main():
global s
handle_argv()
upload_args = struct.pack("!H{}sb5sb".format(len(filename)), 2, filename.encode(), 0, "octet".encode(), 0)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 6666))
s.sendto(upload_args, ("127.0.0.1", 69))
upload_file()
s.close()
if __name__ == "__main__":
main()