【python】socket-传输多个文件、大文件

这篇具有很好参考价值的文章主要介绍了【python】socket-传输多个文件、大文件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0-前言

看过挺多个发文件的例子,但是基本都是发单个,且是 发完连接就结束了

最近正好需要 一个连接 发送 多个文件,根据需求产生以下内容

涉及知识点:socket 的客户端和服务端应用、json、TCP粘包处理

1-发送单个文件流程

  1. 【客户端】获取文件信息

    必备:大小

    可选:文件名、文件绝对路径

  2. 【客户端】准备一个消息,告诉对方我们要发送的内容、属性信息

    {
        "消息类型":"请求发送文件",
        "数据内容":{"大小":123, "文件名":"", "文件绝对路径":"",}
    }
    
  3. 【服务端】收到 “请求发送文件” ,根据文件的内容,做好准备接收,回复可以发送 “request_ack”

  4. 【客户端】开始发送数据

  5. 【服务端】不断接收数据

  6. 【客户端】发送数据结束,发送“quit”

  7. 【服务端】收到“quit”,说明数据发送结束了;回复“quit_ack”表示我方已经接收完毕

socket传输文件大小限制,python,网络,tcp/ip

# 自行准备一个文件的信息字典
{
    "文件路径":{各种属性}
}

# 【以下为示范】
data = {
    "\\HCIA-Security.docx": {
            "绝对路径": "D:\\Users\\Desktop\\HCIA\\HCIA-Security.docx",
            "文件全名": "HCIA-Security.docx",
            "文件名": "HCIA-Security",
            "拓展名": ".docx",
            "最近修改时间": 16786228.8041983,
            "大小": 542353,
            "类型": "文件",
		   "判断结果": "服务端不存在此文件"
        },
}
# 客户端发送文件示范,仅提供思路,直接复制不一定跑的起来,因为这只是我程序里的部分代码

def fasong_dange_wenjian(a_data, a_peer):
    """
        发送单个文件
    :param a_data:单个文件的各种属性
    :param a_peer:socket
    :return:
    """
    # 发送 "请求文件传输" 类型消息,让对端 进入接收文件状态
    temp_json = json.dumps({
        'data_type': "请求文件传输",
        "data": a_data})
    socket.send(temp_json.encode('gbk'))

    if a_data["类型"] == "文件":
        print('--1011发送文件--对方的文件', a_data["对比方的绝对路径"])
        _file_path = ''
        # 等待对方回复可以开始发送
        _request_ack = socket.recv(1024)
        if _request_ack == b'request_ack':  # 确认可发送就开始 发送文件
            _file_path = a_data["绝对路径"]  # 获取这个文件的绝对路径
            print(f"获取这个文件的绝对路径{_file_path}")

            with open(_file_path, "rb") as f:
                """
                    1kB = 1024 字节
                    1MB = 1024 * 1024 = 1048576 字节
                    1GB = 1024 * 1024 * 1024 = 1,073,741,824 字节
                """
                _read_size = 1048576 * 500  # 每次读取 500M,以防文件太大,内存不够报错
                while True:
                    _file_data = f.read(_read_size)
                    if _file_data:
                        socket.sendall(_file_data)  # 发送文件
                    else:
                        print("读取的内容为空,读取结束---")
                        break

            socket.send(b'quit')  # 发送 文件传输结束 信息,说明文件传输结束
            print("1027 文件发送结束")

            _quit_ack = socket.recv(1024)
            if _quit_ack == b'quit_ack':
                print('对方已经收到此文件:', _file_path)
    elif a_data["类型"] == "文件夹":  # 如果是文件夹,我们发送就结束了
        print('1030-发送文件夹--对方的文件夹', a_data["对比方的绝对路径"])
        # 等待对方回复可以开始下个循环
        _dir_ack = socket.recv(1024)
        if _dir_ack == b'dir_ack':
            print('对方已经创建此文件夹:', _dir_ack)


# 循环发送单个文件
for x, y in data.items():  # x 文件相对路径,y 文件各种属性
    if y["判断结果"] == "服务端不存在此文件":
        print("--开始发送单个文件", y["对比方的绝对路径"])
        fasong_dange_wenjian(y, peer)  # 传个文件各种属性 给他就行了
# 服务端接收文件示范,仅提供思路,直接复制不一定跑的起来,因为这只是我程序里的部分代码

# 等待接收服务端的消息
recved = socket.recv(102400)

# json.loads()  将已编码的 JSON 字符串解码为 Python 对象
_temp_b = recved.decode(encoding='gbk')
temp_json = json.loads(_temp_b)

if temp_json['data_type'] == '请求文件传输':  # 收到 客户端 要发送给 服务端请求
    while True:
        # 开始接收数据
        recved = socket.recv(10240)
        _temp_b = recved.decode(encoding='gbk')
        print(f"--数据是: {_temp_b}")
        temp_json = json.loads(_temp_b)
        # temp_json = json.loads(recved.decode(encoding='gbk'))

        if temp_json['data_type'] == '请求文件传输':  # 收到 客户端 要发送给 服务端请求
            print("--821--收到 '请求-文件传输'")

            # "data" 是 对方的绝对路径
            # 判断是不是个文件
            if temp_json["data"]["类型"] == "文件":
                print(f'824--这是文件{temp_json["data"]["对比方的绝对路径"]}')
                # 开始接收文件
            elif temp_json["data"]["类型"] == "文件夹":
                print(f'824--这是文件夹{temp_json["data"]["对比方的绝对路径"]}')
                os.makedirs(temp_json["data"]["对比方的绝对路径"])  # 是目录,直接创建就行了
                print("文件夹创建成功", temp_json["data"]["对比方的绝对路径"])
                socket.send(b'dir_ack')  # 发送信息,说明文件夹创建好了

        elif temp_json['data_type'] == '请求文件传输-结束':  # 文件发送接收,退出 文件传输模式
            break

2-关于发送大文件,本地读取时报错 MemoryError

读取一个4G的文件报了个内存不够的错误

解决方式1:设置每次读取的最大大小

with open(_file_path, "rb") as f:
    """
        1kB = 1024 字节
        1MB = 1024 * 1024 = 1048576 字节
        1GB = 1024 * 1024 * 1024 = 1,073,741,824 字节
    """
    _read_size = 1048576 * 500  # 每次读取 500M,以防文件太大,内存不够报错
    while True:
        _file_data = f.read(_read_size)
        if _file_data:
            socket.sendall(_file_data)  # 发送文件
        else:
            print("读取的内容为空,读取结束---")
            break

解决方式2:更换64位的python

某天发现控制面板里的python是32位,突然想起来32位有最大内存限制,果断卸载换了64位
socket传输文件大小限制,python,网络,tcp/ip
参考大佬文章:
【已解决】Python MemoryError的问题

3-关于粘包

问题背景

一开始没有限制 socket接收的大小

传小文件没啥问题

然后发现在传输 几个G的文件,传输完成后不会执行后面的写入文件,怀疑【服务端】没收到【客户端】传输结束的 “quit”(自定义的退出标志)

排错过程

考虑是不是发太多,程序反应不过来,在【客户端】新增发送文件数据后 等几秒再发送 “quit”(自定义的退出标志)

测试传1G的文件可以了,但是5G的文件又不行了

干脆记录下 【服务端】一共接收了【客户端】的多少数据

"""
	以下是打印 最后2次 接收数据 的记录
	传输的文件大小是 3.8G多 (4096065536字节)
	【客户端】发送的quit 是 4字节
"""

						#  已经接收的大小/文件总大小(单位字节)
接收到数据------4829680 总大小:4095384680/4096065536 
接收到数据------680860  总大小:4096065540/4096065536 

可以非常清楚的看到

最后一次可以接收的大小: 4095384680(总大小) - 4096065536(已经接受的大小) = 680856

实际上最后一次 接收了 680860,刚好多了4字节,而且 【客户端】发送的quit 是 4字节

所以,最终判断,粘包了

解决方案

单个文件,单个连接场景:

发完连接断开就结束了,可以无视

多个文件,只使用一个连接,用for循环 每次发送 单个文件 场景:

  1. 准备3个变量,用于存放以下数据
    1. 获取接收文件的大小信息
    2. 统计已经接收的数据大小
    3. 可接收的剩余数据大小
  2. 实时设置我们还可以接收的大小
    1. 我们接收的缓冲区大小 = 接收文件的总大小 - 已经接收的数据大小
  3. 缓冲区大小,一开始会是文件的大小,但是实际上不可能一次全发完占满,只会一部分一部分发,随着不断接收,会越来越小,直到结束为0
# 【服务端 接收侧】

"""
	准备3个变量
"""
file_size	# 获取接收文件的大小信息
_temp_size = 0   # 统计已经接收的数据大小
recv_size   # 可接收的剩余数据大小


"""
	开始接收
"""
while True:
    # 开始接收文件数据
    if file_size != 0:  # 如果对面 不是个空文件, recv_size != 0
        recv_size = file_size - _temp_size  # 传大文件会发送粘包,导致最后的 quit 黏在 文件数据上,所以实时计算可接收的剩余数据大小
        if recv_size > 0:  # 保证接收的大小在 文件大小之内
            _temp_file_data = socket.recv(recv_size)
            _temp_size += len(_temp_file_data)
            # print(f"接收到数据------{len(_temp_file_data)} 总大小:{_temp_size}/{file_size}  进度:{_temp_size/file_size}")
            print(f"接收到数据------{len(_temp_file_data)}  进度:{(_temp_size / file_size) * 100}")
        elif recv_size == 0:  # 代表文件接收结束,接下来等待 接收 quit
            _temp_file_data = socket.recv(1024)

    elif file_size == 0:  # 如果对面是个空文件, recv_size == 0
        _temp_file_data = socket.recv(1024)
        print("接收到数据------", len(_temp_file_data))

    # 只要不是完成信号,或者数据为0,就一直接收
    if _temp_file_data == b'quit':
        print("接收完毕,quit", "文件总大小 :", file_size)
        break
    elif len(_temp_file_data) == 0:
        print("这是空数据---quit---")
        break

    _file_date += _temp_file_data  # 把数据先存入变量

# 写入文件【你也可以设置收到数据就写入文件,节省内存,但是我不缺内存,选择先存在内存 后写入储存】
with open(_file_path, "wb+") as f:
    f.write(_file_date)
    print(f"{_file_route} 写入成功")

# 接受完成标志
socket.send(b'quit_ack')

4-备注-换算表

换算表【数据源自百度百科】

中文单位 中文简称 英文单位 英文简称 进率(Byte=1)
比特 比特 bit b 0.125
字节 字节 Byte B 1
千字节 千字节 KiloByte KB 2^10
兆字节 MegaByte MB 2^20
吉字节 GigaByte GB 2^30
太字节 Terabyte TB 2^40
拍字节 PetaByte PB 2^50
艾字节 ExaByte EB 2^60
泽字节 ZettaByte ZB 2^70
尧字节 YottaByte YB 2^80
千亿亿亿字节 千亿亿亿字节 BrontByte BB 2^90

若是用普通网速1Mbs计算,约8秒钟能下载1MB的文件。

1b(bit,位 或 比特,b)

1B(Byte,字节,B)=8bit

1KB( Kilobyte,千字节,KB)=1024B

1MB( Megabyte,兆字节,MB)=1024KB

1GB( Gigabyte,吉字节,GB)=1024MB

换算:

1kB = 1024 字节
1MB = 1024 * 1024 = 1048576 字节
1GB = 1024 * 1024 * 1024 = 1073741824 字节

存放的内容:文章来源地址https://www.toymoban.com/news/detail-627699.html

bit(位 或 比特)
一个二进制位只可以表示0和1两种状态(2的1次方);

Byte(字节)
习惯上用大写的“B”表示。
字节是计算机中数据处理的基本单位。
一个字节由八个二进制位构成,即1个字节等于8个比特 (1Byte=8bit) 。
八位二进制数最小为00000000,最大为11111111;
通常1个字节可以存入一个ASCII码,2个字节可以存放一个汉字国标码。

到了这里,关于【python】socket-传输多个文件、大文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Socket实例,实现多个客户端连接同一个服务端代码&TCP网络编程 ServerSocket和Socket实现多客户端聊天

    Java socket(套接字)通常也称作\\\"套接字\\\",用于描述ip地址和端口,是一个通信链的句柄。应用程序通常通过\\\"套接字\\\"向网络发出请求或者应答网络请求。 使用socket实现多个客户端和同一客户端通讯;首先客户端连接服务端发送一条消息,服务端接收到消息后进行处理,完成后再

    2024年02月12日
    浏览(76)
  • Socket 传情:用 Python 编织 TCP 网络

    项目 描述 Python 官方文档 https://docs.python.org/zh-cn/3/ 搜索引擎 Google 、Bing 项目 描述 操作系统 Windows 10 专业版 PyCharm 2023.1 (Professional Edition) Python 3.10.6 服务器端 创建一个监听套接字对象,指定地址族( IPV4 或 IPV6 )和套接字类型( TCP 套接字 或 UDP 套接字 )。 通过使用 bind()

    2024年02月09日
    浏览(37)
  • Linux网络编程之TCP文件传输

    1. 要求 在Linux环境下,编程实现文件的上传和下载,即客户端可以发送文件给服务器,服务器将文件写到服务器端文件系统中;客户端请求下载文件时服务器读取文件内容,发送给客户端,客户端接收内容并写入本地文件。要求 (1)源代码格式化良好并适当注释; (2)除上述核心功

    2024年02月08日
    浏览(43)
  • Linux下 TCP 连接限制如何解除,解决socket高并发连接数限制,tcp默认1024个连接

    苏州私有云 当用户使用Linux作为系统时,socket在运行高并发的TCP程序时,可能会遇到连接数量到一定个数后被阻断的情况 我在工作的时候,测试高并发tcp程序(GPS服务器端程序),经过多次测试之后,发现每次建立的连接到达1000个左右 就再也不能建立tcp连接,然后在互联网

    2024年02月16日
    浏览(102)
  • 拥塞控制(TCP限制窗口大小的机制)

            拥塞控制机制可以使滑动窗口在保证可靠性的前提下,提高传输效率         关于滑动窗口的属性以及部分机制推荐看TCP中窗口和滑动窗口的含义以及流量控制         看了上面推荐的博客我们已经知道了,由于接收方接收数据的能力有限,所以要通过流量控制的

    2024年02月11日
    浏览(49)
  • TCP、UDP数据包大小的限制

    已剪辑自: https://www.cnblogs.com/ellisonzhang/p/10402863.html 1、概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层。 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Data)在应用层 它们的关系是 数据帧{IP包{TCP或UDP包

    2024年02月11日
    浏览(32)
  • 使用 python socket 实现UDP/TCP网络通信

    目录 目录 1.socket简介 2.创建socket 2.1创建UDPSocket 2.2创建TCPSocket 3.使用UDPSocket发送数据并接收 4.使用UDPSocket发送广播 5.UDPSocket聊天器 (多线程实现消息的收发功能) 6.使用TCPSocket建立客户端 7.使用TCPSocket建立服务端        socket(简称:套接字),是支持TCP和UDP(网络传输方式

    2023年04月10日
    浏览(65)
  • Qt开发-TCP/IP网络通信(以及文件传输)

    TCP/IP通信(即SOCKET通信)是通过网线将 服务器Server端 和 客户机Client端 进行连接,在遵循ISO/OSI模型的四层层级构架的基础上通过TCP/IP协议建立的通讯。控制器可以设置为服务器端或客户端。 关于TCP/IP协议可详看:TCP/IP协议详解 - 知乎 (zhihu.com) 总的来说,TCP/IP通讯有两个部分

    2024年02月10日
    浏览(52)
  • 《TCP/IP网络编程》--基于TCP实现字符串对话和文件传输

    主要需求:         服务器端和客户端各传递 1 次字符串,基于 TCP 协议,传递字符串前先以 4 字节整数型方式传递字符串长度,剩余部分为字符串数据; 注:下面的代码基于 Windows 系统实现; 项目链接:Chapter5

    2024年02月09日
    浏览(55)
  • Python网络编程基础之ip地址,端口号,TCP,socket

    IP地址 IP地址 (Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。 换而言之,IP 地址就是标识网络中设备的一个地址,好比

    2024年02月02日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包