一、说明

  本文主要基于socket实现TFTP文件上传与下载。

  测试环境:Win10/Python3.5/tftpd64。

  tftpd下载:根据自己的环境选择下载,地址 :http://tftpd32.jounin.net/tftpd32_download.html

  主要内容:TFTP协议介绍、程序运行图示和分析fmt、源代码。

二、TFTP协议介绍(参考网络,详情可搜索)

  TFTP(Trivial File Transfer Protocol,简单文件传输协议),是TCP/IP协议族中的一个用来在客户端与服务端(C/S架构)之间进行文件传输的协议。

  1、特点:

    > 简单、占用资源少

    > 适合小文件传输

    > 适合在局域网中进行传输

    > 端口号为

    > 基于UDP实现

  

  2、TFTP下载过程分析:

    当打开一个tftpd作为服务端,会默认监听69端口,所以客户端发送数据到服务端都是经过69端口。

    

    下载的数据流过程如上图所示,客户端首次发送需要下载的文件名到服务端,(文件存在)服务端收到后会返回该文件的第一个包,客户端收到后本地保存然后再发送ACK应答包给服务端,如此往来多次,一发一答,即实现了文件的下载。

  3、TFTP操作码与数据格式:

    

      

  4、差错码以及对应的提示:

    

    

  5、TFTP上传过程分析(此处做简单文件说明,可参考下面源码或自行搜索):

    上传的基本流程:客户端发送写请求(操作码为2)到服务端,如果可以进行上传,服务端会返回ACK应答包,客户端收到后即可进行第一个数据包发送,进而服务端收到后会返回ACK应打包,如此多次,当客户端文件读取完成,即可退出上传,此时上传完成。

三、程序运行图示和分析fmt

  1、运行起来的tftpd服务端如下所示:

    选择作为下载的路径和配置IP

    

  2、下载过程:

    运行脚本传入两个参数:服务端IP和文件名

    

  3、上传过程:

     

  4、关于struct.pack() 和 struct.unpack()的参数说明:

    参考:https://blog.csdn.net/DaxiaLeeSuper/article/details/82018070

    struct.pack(b"!H7sb5sb", b"test.png", 0, b"octet", 0 )

    主要分析第一个参数 fmt:如 “!H7sb5sb" => [ 1, b"test.png", 0, b"octet", 0  ]

    fmt对后面几个参数说明,其中H代表1,7s表示长度为7的字符串等

    

    1、fmt首个字符:

      

    2、fmt其他字符:

       

  

四、源码

 # -*- coding:utf-8 -*-

 """
实现 TFTP 上传与下载功能
需要配合tftpd 软件测试
""" from socket import *
import struct
import sys class DownloadClient:
"""
下载基本流程:
--------------------------------------
客户端(Client) 服务端(Server)
--------------------------------------
读写请求 --->
<--- 数据包[0]
ACK[0] --->
<--- 数据包[1]
ACK[1] --->
....
-------------------------------------- 操作码 功能
--------------------------------------
1 读请求,即下载
2 写请求,即上传
3 表示数据包,即Data
4 确认码,即ACK
5 错误
--------------------------------------
"""
def __init__(self):
# 读取参数
if len(sys.argv) != 4:
print("-" * 30)
print("Tips:")
print("python xxx.py 1 127.0.0.1 test.png")
print("-" * 30)
exit()
else:
self.mid = sys.argv[1] # 执行的方法,1下载或2上传
self.remoteIp = sys.argv[2] # 服务器IP
self.filename = sys.argv[3] # 下载文件名 # 创建socket实例
self.socketClient = socket(AF_INET, SOCK_DGRAM)
self.socketClient.bind(('', 7788)) def start(self):
"""启动执行"""
if self.mid == "":
self.download()
elif self.mid == "":
self.upload()
else:
print(self.mid)
print("参数输入错误 [python 脚本名 方法id(1下载,2上传) 服务器IP 文件名]:python xxx.py 1 127.0.0.1 test.png")
exit() def download(self):
""" TFTP 下载"""
print("下载启动...") # 构建下载请求数据
# 第一个参数 !H7sb5sb = "!H"+str(len(filename))+"sb5sb"
filenameLen = str(len(self.filename))
cmdBuf = struct.pack(("!H%ssb5sb" % filenameLen).encode("utf-8"), 1, self.filename.encode("utf-8"), 0, b"octet", 0) # 发送下载文件请求数据到指定服务器
self.socketClient.sendto(cmdBuf, (self.remoteIp, 69)) # self.show() locPackNum = 0 # 请求包号
saveFile = '' # 保存文件句柄
while True:
recvData, recvAddr = self.socketClient.recvfrom(1024)
recvDataLen = len(recvData) # 解包
cmdTuple = struct.unpack(b"!HH", recvData[:4])
cmd = cmdTuple[0] # 指令
curPackNum = cmdTuple[1] # 当前包号 if cmd == 3: # 是否为数据包
if curPackNum == 1:
# 以追加的方式打开文件
saveFile = open(self.filename, "ab") # 包编号是否和上次相等
if locPackNum + 1 == curPackNum:
saveFile.write(recvData[4:]) # 写入数据
locPackNum += 1 # 发送ACK应答
ackBuf = struct.pack(b"!HH", 4, locPackNum)
self.socketClient.sendto(ackBuf, recvAddr) print("(%d)次接收到数据" % locPackNum) # 确认为最后一个包
if recvDataLen < 516:
saveFile.close()
print("已经下载完成")
break elif cmd == 5: # 是否为错误应答
print("error num:%d" % curPackNum)
break def upload(self):
"""TFTP 上传"""
print("上传启动...") # 1、发送读请求
filenameLen = str(len(self.filename))
cmdBuf = struct.pack(("!H%ssb5sb" % filenameLen).encode("utf-8"), 2, self.filename.encode("utf-8"), 0, b"octet", 0) self.socketClient.sendto(cmdBuf, (self.remoteIp, 69)) localPackNum = 1 # 本地包号
sendFile = '' # 文件句柄
while True:
# 2、接收回复
recvData, recvAddr = self.socketClient.recvfrom(1024) # 3、解包
cmdTuple = struct.unpack(b"!HH", recvData[:4])
cmd = cmdTuple[0] # 指令
curPackNum = cmdTuple[1] # 当前包号 # print(recvData) if cmd == 4:
# 打开并读取文件
if curPackNum == 0:
sendFile = open(self.filename, "rb") # ACK应答的包号是否与本地的一样
if localPackNum - 1 == curPackNum:
# 4、读取 512 byte数据
sendData = sendFile.read(512) # 判断文件是否读取完成
if len(sendData) <= 0:
sendFile.close()
print("上传完成")
break # 5、打包发送数据
sendDataBuf = struct.pack(b"!HH512s", 3, localPackNum, sendData)
self.socketClient.sendto(sendDataBuf, recvAddr) # 打印过程
print("(%d)次已发送,数据长度:%d" % (localPackNum, len(sendData)))
localPackNum += 1 elif cmd == 5:
# sendFile.close()
print("error num:%d" % curPackNum)
break def show(self):
"""测试打印数据"""
recvData = self.socketClient.recvfrom(1024)
print(recvData)
exit() if __name__ == "__main__":
demo = DownloadClient()
demo.start()

[Python] socket实现TFTP上传和下载的更多相关文章

  1. 【Python学习 】Python实现的FTP上传和下载功能

    一.背景 最近公司的一些自动化操作需要使用Python来实现FTP的上传和下载功能.因此参考网上的例子,撸了一段代码来实现了该功能,下面做个记录. 二.ftplib介绍 Python中默认安装的ftp ...

  2. Python——操作smb文件服务器(上传和下载)

    最近在做上传和下载,然后文件比较大和多,就用到了文件服务器,文件服务器是实体机 ,不是在本地, 然后用python 通过pysmb模块就可以直接进行操作 mac选择前往.连接服务器去查看文件服务器里都 ...

  3. [python][flask] Flask 图片上传与下载例子(支持漂亮的拖拽上传)

    目录 1.效果预览 2.新增逻辑概览 3.tuchuang.py 逻辑介绍 3.1 图片上传 3.2 图片合法检查 3.3 图片下载 4.__init__.py 逻辑介绍 5.upload.html ...

  4. Python Socket实现文件上传(TCP协议)

    在TCP协议下通过socket模块实现文件上传 #!/usr/bin/env python # -*- coding: utf-8 -*- # desc: tcp_server_file_upload ...

  5. Tftp上传、下载

    上传 tftp -g -r filename serverip 下载 tftp -p -l filename serverip

  6. shell 和python 实现ftp文件上传或者下载

    一.shell脚本 #####从ftp服务器上的/home/data 到 本地的/home/databackup#####!/bin/bashftp -n<<!open 172.168.1 ...

  7. python ssh登录linux 上传和下载文件

    #!usr/bin/python# coding: utf-8 import paramikoimport jsonremotedir='/tmp/log'remotefile = 'bst_mana ...

  8. python之实现ftp上传下载代码(含错误处理)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之实现ftp上传下载代码(含错误处理) #http://www.cnblogs.com/kait ...

  9. 使用python操作FTP上传和下载

    函数释义 Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件,函数列举如下 ftp登陆连接 from ftplib import F ...

随机推荐

  1. 从国际象棋与象棋的走法差异,再趣说IT人提升能力和增收方式

    之前我写过篇博文,用象棋的思维趣说IT人的职业发展和钱途,发现象棋中的一些思维能应用到我们程序员平时的职业发展中. 当从大学毕业的程序员干个五六年以后,也达到了高级开发的水平,工作环境应该能摆脱动荡, ...

  2. java练习---8

    //程序员:罗元昊 2017.10.16 题目3.7 import java.util.Scanner; public class L { @SuppressWarnings("resour ...

  3. SpringMVC简易架构图。。。

    DispatcherServlet拦截所有请求 -> 通过访问url路径找到对应的控制器 -> 通过适配器调用控制器的方法 -> 控制器里面的方法处理业务 -> 通过视图解析器 ...

  4. Android的步骤

    1.开发Activity步骤 第一步:写一个类继承Activity 第二步:重写oncreate方法 第三步:在主配置文件中注册activity <activity android:name=& ...

  5. 私有网络(VPC)概述

    1 什么是私有网络(VPC) 私有网络是一块可用户自定义的网络空间,您可以在私有网络内部署云主机.负载均衡.数据库.Nosql快存储等云服务资源.您可自由划分网段.制定路由策略.私有网络可以配置公网网 ...

  6. 主机cpu突然飙高,如何快速排查问题

    [问题发现] 使用zabbix软件监控服务器时发现cpu突然异常,在业务主机上使用top命令查看系统的整体运行情况,使用top命令后发现mysqld占用CPU特别高,初步判断可能是mysqld出现问题 ...

  7. 前端工程师和设计师必备的chrome插件

    Google Chrome是最好用的几个浏览器之一,今天我来分享下自己收集的一系列Chrome插件,希望对大家的学习和工作有帮助. 注:你可以通过复制链接或者在谷歌商店搜索相应插件的名称来获取以下插件 ...

  8. react开发中的小细节

    目前开始使用react余遇到的问题还不是很多,但还是希望总结一下. react中的属性prop: 在react中组件的父子组件的通信是基于prop的,当然对于底层的东西不是特别了解,但可以说一说它的基 ...

  9. Linux常用的命令及使用方法

    1.请用命令查出ifconfig命令程序的绝对路径 [root@localhost ~]# which ifconfig(ifconfig是linux中用于显示或配置网络设备(网络接口卡)的命令) / ...

  10. LFS8.3BOOT引导疑点解决

    LFS系统 的BOOT引导 在LFS书中写到的BOOT引导,时直接将宿主机的BOOT分区挂载当LFS的BOOT分区中,虽然这样也是可以实现BOOT引导的,但是我并不想这样做,所以BOOT引导就变得有些 ...