美文网首页Python新世界python热爱者
Python Web开发属于自己的网盘,socket实现文件上传

Python Web开发属于自己的网盘,socket实现文件上传

作者: 48e0a32026ae | 来源:发表于2018-11-26 15:18 被阅读4次

    一、说明

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

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

    学习Python中有不明白推荐加入交流群

                    号:516107834

                    群里有志同道合的小伙伴,互帮互助,

                    群里有不错的学习教程!

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

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

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

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

    1、特点:

    > 简单、占用资源少

    > 适合小文件传输

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

    > 端口号为 69

    > 基于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其他字符:

    四、源码

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

    2

    3 """

    4 实现 TFTP 上传与下载功能

    5 需要配合tftpd 软件测试

    6 """

    7

    8 from socket import *

    9 import struct

    10 import sys

    11

    12

    13 class DownloadClient:

    14 """

    15 下载基本流程:

    16 --------------------------------------

    17 客户端(Client) 服务端(Server)

    18 --------------------------------------

    19 读写请求 --->

    20 <--- 数据包[0]

    21 ACK[0] --->

    22 <--- 数据包[1]

    23 ACK[1] --->

    24 ....

    25 --------------------------------------

    26

    27 操作码 功能

    28 --------------------------------------

    29 1 读请求,即下载

    30 2 写请求,即上传

    31 3 表示数据包,即Data

    32 4 确认码,即ACK

    33 5 错误

    34 --------------------------------------

    35 """

    36 def __init__(self):

    37 # 读取参数

    38 if len(sys.argv) != 4:

    39 print("-" * 30)

    40 print("Tips:")

    41 print("python xxx.py 1 127.0.0.1 test.png")

    42 print("-" * 30)

    43 exit()

    44 else:

    45 self.mid = sys.argv[1] # 执行的方法,1下载或2上传

    46 self.remoteIp = sys.argv[2] # 服务器IP

    47 self.filename = sys.argv[3] # 下载文件名

    48

    49 # 创建socket实例

    50 self.socketClient = socket(AF_INET, SOCK_DGRAM)

    51 self.socketClient.bind(('', 7788))

    52

    53 def start(self):

    54 """启动执行"""

    55 if self.mid == "1":

    56 self.download()

    57 elif self.mid == "2":

    58 self.upload()

    59 else:

    60 print(self.mid)

    61 print("参数输入错误 [python 脚本名 方法id(1下载,2上传) 服务器IP 文件名]:python xxx.py 1 127.0.0.1 test.png")

    62 exit()

    63

    64 def download(self):

    65 """ TFTP 下载"""

    66 print("下载启动...")

    67

    68 # 构建下载请求数据

    69 # 第一个参数 !H7sb5sb = "!H"+str(len(filename))+"sb5sb"

    70 filenameLen = str(len(self.filename))

    71 cmdBuf = struct.pack(("!H%ssb5sb" % filenameLen).encode("utf-8"), 1, self.filename.encode("utf-8"), 0, b"octet", 0)

    72

    73 # 发送下载文件请求数据到指定服务器

    74 self.socketClient.sendto(cmdBuf, (self.remoteIp, 69))

    75

    76 # self.show()

    77

    78 locPackNum = 0 # 请求包号

    79 saveFile = '' # 保存文件句柄

    80 while True:

    81 recvData, recvAddr = self.socketClient.recvfrom(1024)

    82 recvDataLen = len(recvData)

    83

    84 # 解包

    85 cmdTuple = struct.unpack(b"!HH", recvData[:4])

    86 cmd = cmdTuple[0] # 指令

    87 curPackNum = cmdTuple[1] # 当前包号

    88

    89 if cmd == 3: # 是否为数据包

    90 if curPackNum == 1:

    91 # 以追加的方式打开文件

    92 saveFile = open(self.filename, "ab")

    93

    94 # 包编号是否和上次相等

    95 if locPackNum + 1 == curPackNum:

    96 saveFile.write(recvData[4:]) # 写入数据

    97 locPackNum += 1

    98

    99 # 发送ACK应答

    100 ackBuf = struct.pack(b"!HH", 4, locPackNum)

    101 self.socketClient.sendto(ackBuf, recvAddr)

    102

    103 print("(%d)次接收到数据" % locPackNum)

    104

    105 # 确认为最后一个包

    106 if recvDataLen < 516:

    107 saveFile.close()

    108 print("已经下载完成")

    109 break

    110

    111 elif cmd == 5: # 是否为错误应答

    112 print("error num:%d" % curPackNum)

    113 break

    114

    115 def upload(self):

    116 """TFTP 上传"""

    117 print("上传启动...")

    118

    119 # 1、发送读请求

    120 filenameLen = str(len(self.filename))

    121 cmdBuf = struct.pack(("!H%ssb5sb" % filenameLen).encode("utf-8"), 2, self.filename.encode("utf-8"), 0, b"octet", 0)

    122

    123 self.socketClient.sendto(cmdBuf, (self.remoteIp, 69))

    124

    125 localPackNum = 1 # 本地包号

    126 sendFile = '' # 文件句柄

    127 while True:

    128 # 2、接收回复

    129 recvData, recvAddr = self.socketClient.recvfrom(1024)

    130

    131 # 3、解包

    132 cmdTuple = struct.unpack(b"!HH", recvData[:4])

    133 cmd = cmdTuple[0] # 指令

    134 curPackNum = cmdTuple[1] # 当前包号

    135

    136 # print(recvData)

    137

    138 if cmd == 4:

    139 # 打开并读取文件

    140 if curPackNum == 0:

    141 sendFile = open(self.filename, "rb")

    142

    143 # ACK应答的包号是否与本地的一样

    144 if localPackNum - 1 == curPackNum:

    145 # 4、读取 512 byte数据

    146 sendData = sendFile.read(512)

    147

    148 # 判断文件是否读取完成

    149 if len(sendData) <= 0:

    150 sendFile.close()

    151 print("上传完成")

    152 break

    153

    154 # 5、打包发送数据

    155 sendDataBuf = struct.pack(b"!HH512s", 3, localPackNum, sendData)

    156 self.socketClient.sendto(sendDataBuf, recvAddr)

    157

    158 # 打印过程

    159 print("(%d)次已发送,数据长度:%d" % (localPackNum, len(sendData)))

    160 localPackNum += 1

    161

    162 elif cmd == 5:

    163 # sendFile.close()

    164 print("error num:%d" % curPackNum)

    165 break

    166

    167 def show(self):

    168 """测试打印数据"""

    169 recvData = self.socketClient.recvfrom(1024)

    170 print(recvData)

    171 exit()

    172

    173

    174 if __name__ == "__main__":

    175 demo = DownloadClient()

    176 demo.start()

    相关文章

      网友评论

        本文标题:Python Web开发属于自己的网盘,socket实现文件上传

        本文链接:https://www.haomeiwen.com/subject/oxybqqtx.html