python网络基础:STRUCT模块定制报文,实现FTP上传功能

一、struct模块

python中struct 模块用于python数据结构与C结构之间的相互转换,其中C结构是用一种格式化字符串表示的,学习struct 模块的难点就在这个格式化字符串上,强烈建议最好了解下C语言结构体的相关知识点,如果比较熟悉C语言结构体及对齐,学习struct 模块轻而易举。
官方英文文档:struct — Interpret strings as packed binary data
引用来源:https://blog.csdn.net/guoyajie1990/article/details/81044929

二、自制报文格式

headers = {‘file_name’: filename, ‘file_path’: filepath, ‘file_size’: None}

类型 方法 描述
报头 head=struct.pack(‘i’, ‘序列化之后报文的长度’) 长度4个,服务端接受时候可以根据这个设置接受长度,避免沾包
发送报头 send(head) 服务端可以根据4个字节获取报头
发送报文 send(‘序列化之后的headers’) 服务端根据报文内文件大小,设置接受长度,避免沾包

网络传输过程中处处有协议,协议就是一堆报头和报文,协议的解析过程不需要关心,协议本质上就是一种约定

FTP上传功能实例

1、服务端代码

代码均有注释,方便理解,如有不懂,评论或者私信,一起学习一起进步

import socket
import struct,pickle

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 获取tcp/ip套接字
tcpSock.bind(('127.0.0.1', 8898))  # 绑定 (主机,端口号)到套接字
tcpSock.listen(1)  # 开始监听,设置半进程池为1

print('====服务端等待请求中====')
while True:
    conn, addr = tcpSock.accept()  # 4、被动(阻塞式)接受TCP客户的连接
    print('新用户:', addr)

    # 上传功能
    while True:
        try:
            head_len = conn.recv(4)  # 接受报头长度,报头均为4个字节
            if not head_len:break  # 如果传入的数据为空,容易造成卡顿。
            head_len = struct.unpack('i', head_len)[0]  #报文的长度
            bytes_head = conn.recv(head_len)
            if not bytes_head: break
            headers = pickle.loads(bytes_head)  # 将字典反序化为字符串
            file_size = int(headers['file_size']) # 获取上传文件大小


            with open(headers['file_name'], 'wb')as f:
                while file_size:
                    if file_size >= 1024:
                        f.write(conn.recv(1024))
                        file_size -= 1024
                    else:
                        f.write(conn.recv(file_size))
                        break
                print('收到用户:%s 上传的:%s 文件' %(addr, headers['file_name']))

        except ConnectionResetError:
            print('===开始连接新用户===')
            break

    conn.close()

tcpSock.close()  # 回收系统资源

2、客户端代码

from socket import *
import os, pickle, struct

Client = socket(AF_INET, SOCK_STREAM)  # 获取tcp/ip套接字
Client.connect(('127.0.0.1', 8898))   # 请求服务器 (主机,端口号)到套接字


filepath = input('请输入文件路径:')
filename = input('请输入文件名')

# filepath = r'C:\Users\Administrator\Desktop\Day\Jasn--70--Days\leetcode刷题'
# filename = r'01两数之和.py'

# 设置报头格式(报文)
headers = {'file_name': filename, 'file_path': filepath, 'file_size': None}
file_path = os.path.join(headers['file_path'], headers['file_name'])  # 拼接绝对路径,获取上传文件路径
file_size = os.path.getsize(file_path)  # 获取上传文件大小
headers['file_size'] = file_size

bytes_head = pickle.dumps(headers)  # 字典转bytes
head_len = len(bytes_head)
pack_len = struct.pack('i', head_len) # 制作报头
Client.send(pack_len)  # 先传报头长度 ,长度一般为4个字节
Client.send(bytes_head)  # 再传报文信息

# 上传文件
with open(file_path, 'rb')as f:
    while file_size:
        if file_size >= 1024:  # 一次限制上传内容为1024字节
            cont = f.read(1024)  
            Client.send(cont)
            file_size -= 1024
        else:
            cont = f.read(file_size)
            Client.send(cont)
            break

Client.close() # 回收套接字资源

知识点:
read([size])方法:从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象。

发布了72 篇原创文章 · 获赞 79 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42444693/article/details/104861139