Python中TFTP的理解

Num01–>TFTP协议介绍

TFTP(Trivial File Transfer Protocol,简单文件传输协议)

是TCP/IP协议族中的一个用来在客户端与服务器之间进行简单文件传输的协议

特点:

1,简单
2,占用资源小
3,适合传递小文件
4,适合在局域网进行传递
5,端口号为69
6,基于UDP实现

Num02–>TFTP下载过程

TFTP服务器默认监听69号端口

当客户端发送“下载”请求(即读请求)时,需要向服务器的69端口发送

服务器若批准此请求,则使用一个新的、临时的 端口进行数据传输

这里写图片描述

当服务器找到需要现在的文件后,会立刻打开文件,把文件中的数据通过TFTP协议发送给客户端

如果文件的总大小较大(比如3M),那么服务器分多次发送,每次会从文件中读取512个字节的数据发送过来

因为发送的次数有可能会很多,所以为了让客户端对接收到的数据进行排序,所以在服务器发送那512个字节数据的时候,会多发2个字节的数据,用来存放序号,并且放在512个字节数据的前面,序号是从1开始的

因为需要从服务器上下载文件时,文件可能不存在,那么此时服务器就会发送一个错误的信息过来,为了区分服务发送的是文件内容还是错误的提示信息,所以又用了2个字节 来表示这个数据包的功能(称为操作码),并且在序号的前面

操作码     功能
 1      读请求,即下载
 2      写请求,即上传
 3      表示数据包,即DATA
 4      确认码,即ACK
 5      错误

因为udp的数据包不安全,即发送方发送是否成功不能确定,所以TFTP协议中规定,为了让服务器知道客户端已经接收到了刚刚发送的那个数据包,所以当客户端接收到一个数据包的时候需要向服务器进行发送确认信息,即发送收到了,这样的包成为ACK(应答包)

为了标记数据已经发送完毕,所以规定,当客户端接收到的数据小于516(2字节操作码+2个字节的序号+512字节数据)时,就意味着服务器发送完毕了

Num03–>TFTP数据包的格式

这里写图片描述

Num04–>TFTP客户端案例编写

#! /usr/bin/env python3
# -*- coding:utf-8 -*- 

from socket import *
import struct #为了实现打包struct.pack()和拆包struct.unpack()数据
import sys

# python3 05-xx.py 192.168.105.125 bb.jpg
def main():

    if len(sys.argv) < 3:
        sys.exit('usage : python3  %s ip filename' % sys.argv[0])

    #server_ip = '192.168.105.125'
    #file_name = 'bb.jpg'

    server_ip = sys.argv[1]
    file_name = sys.argv[2]

    udp_socket = socket(AF_INET, SOCK_DGRAM)
    server_addr = (server_ip,69)

    #  打包数据
    # !表示网络字节序,H表示2bytes无符号整数,
    #  5s表示长度为5字符串
    #  B表示1byte的无符号整数
    fmt = '!H%dsB5sB' % len(file_name)
    send_data = struct.pack(fmt,1,file_name.encode() ,0,b'octet',0)
    #send_data = struct.pack(fmt,1,file_name ,0,b'octet',0)

    udp_socket.sendto(send_data,server_addr)

    f =  None # 文件对象
    #上一次blockNum
    lastBlockNum = 0

    # 循环接收和应答
    while True:
        recv_data,peer_addr = udp_socket.recvfrom(1024)
        # 拆包数据
        opcode,blockNum = struct.unpack('!HH',recv_data[:4])

        if opcode == 3: # 表示数据包
            # 写入文件
            # 1打开文件
            # 第一次收到服务器发送数据包
            if blockNum == 1: 
                f = open(file_name,'wb')

            # 拆出数据
            data_fmt = '!%ds' % (len(recv_data) - 4)
            data_content = struct.unpack(data_fmt, recv_data[4:])

            # 写入文件之前判断写过没有
            # if 这一次blockNum == 上一次blockNum + 1
            if lastBlockNum + 1 == blockNum:
                #print(data_content[0])
                f.write(data_content[0]) # 拆出来是元组,bytes对象,write时候需要str字符串

            # 打包应答数据
            ack_data = struct.pack('!HH',4,blockNum)
            udp_socket.sendto(ack_data,peer_addr) # 不能再给server_addr,因为端口号变了

            # 当应答完毕,更新lastBlockNum
            lastBlockNum = blockNum

            # 如果数据长度小于 2 + 2 + 512 传输结束
            if len(recv_data) < 516:
                print('over')
                f.close()
                break
        elif opcode == 5:# 出错
            err_num = blockNum
            # 拆出错误信息
            fmt = "!%ds" % (len(recv_data) - 5)
            err_msg = struct.unpack(fmt,recv_data[4:-1])
            print('出错信息:%s' % err_msg)
            break

if __name__ == "__main__":
    main()
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值