| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> 基于TCP/UDP/FTP实现的PySide2文件传输工具 -> 正文阅读 |
|
[网络协议]基于TCP/UDP/FTP实现的PySide2文件传输工具 |
引言????????基于PySide2开发可视化文件传输程序界面,用户可以选择TCP/UDP/FTP类型协议在局域网中进行传输文件,用户一端既可以接收文件,也可以发送文件。做这个项目的时候,用了差不多一个多月的时间,目前实现了TCP和UDP,暂不支持传输多个文件(不过拓展起来很方便)!FTP协议后续新增跟上... 项目历程第一个方案? ? ? ? UI在主线程运行,开启子线程来进行文件发送和接收。也就是说,子线程中既要等待客户端连接,又要负责文件接收,只能作为客户端或者服务端,因此用户一端只能发数据或者收数据。因为使用到可视化界面设计,在构思功能时,希望UI可以通过某个按钮主动关闭服务端或客户端(只结束子线程,不结束主线程)。 ? ? ? ? 然而,子线程中socket的recv、accept方法默认是阻塞的,主线程这边没有办法让子线程结束(子线程在while循环工作,加了标记位,但是子线程没机会判断到),即使把子线程设为守护线程,结果还是关闭不了。进一步,主线程先开启子线程,子线程再开启子线程(孙线程,设为守护线程),由主线程关闭子线程,孙线程还是无法结束(守护线程并非跟随父线程)。 第二个方案? ? ? ? 为了让子线程有机会判断到全局标记位,让子线程自己结束自己,那么可以把recv、accept方法设成非阻塞状态,即setblocking(0)。服务端accept等待客户端没有连接时,会一直抛出BlockingIOError异常,然后检查标记位,判断是否要结束子线程,直到有客户端连接才接着执行后面的程序;同样把recv方法放在while循环,在没有收到数据的时候,让它有机会判断全局标记位。
????????最后确认这种方法有效,但是文件传输速度很慢(TCP),80M的文件居然耗时26s,后面经过调整socket的收发缓冲区大小(其实可以不用设置缓冲区大小,默认还是65536B,只是往send方法里传入50M的数据,recv(50M)这样子),从1024B到1M再到50M,花了0.2s的时间就传完了80M的文件,包括文件的读写过程。TCP是基于流通信的,应用端只管把数据提交到系统层,即使是100M也没影响,底层会把数据分成一段一段发过去(最大1500B,网卡的MTU限制),数据最终是能够达到目标地址。 ? ? ? ? 在这个过程有很多东西要交互到UI,UI在主线程,而TCP在子线程,子线程想把打印显示到UI,基于这个需求,我自己封装了一个QT Signal类,当子线程有数据打印时,通过Signal信号触发UI对应的槽函数,就能把打印显示到UI了,这在以后文件传输的进度条显示上也使用到了。
? ? ? ? 方案二运行起来唯一缺点就是耗性能,因为在非阻塞模式,如果没有客户端连接或没有数据发送过来,就一直会抛异常,这样并不好。 第三个方案?? ? ? ? 为了UI能结束TCP子线程,而不用把TCP Socket设为非阻塞,也就是说,我需要的只是结束子线程,主线程(UI)不用结束,经过想了想之后,决定用多进程解决这些问题; ????????用进程的好处,不用等待其他子线程或子进程执行完,就能立即结束掉(子进程结束后,所有的子进程资源都会被销毁),需要工作时再创建开启子进程。比方案二好就是不用再考虑阻不阻塞的问题。 ?????????程序运行起来后,只有一个UI主进程,在初始化的时候创建一个UI子线程,用于和TCP子进程交换进程的通信数据。TCP子进程只有在用户点击监听(作为服务端)或点击发送(作为客户端)时,才会被创建和开启。TCP子进程里又开启两个子线程(同时开启运行),一个作为服务端,立即开启等待客户端连接(等待客户端发送文件过来);另一个作为客户端,需要等待UI主进程的发送文件信号,才会开启连接对方的服务端。这两个子线程在接收或发送完文件后,又回到等待连接或发送状态,整个子进程随时都有可能被UI主进程主动结束。
???????当用户点击发送按钮时,发送时,先连接对方的服务端(另一端服务是开启监听状态下),接着计算发送的文件MD5; 客户端开始发送文件的头部信息(文件名、文件大小、文件的MD5)过去给服务端;等待服务端的响应后(确认收到文件头部信息后),接着发送文件的内容; ????????服务端监听到有客户端连接了,就准备接收文件头部信息,收到文件头部信息后,答应给客户端确认收到了,接着准备收文件内容;接收文件保存完成后,开始计算保存的文件MD5;
????????如果保存的文件MD5值和接收文件头部信息里MD5的值一样,则该文件是安全、有效的。 ????????TCP一次收发数据的大小(可修改的),是服务端和客户端先固定好的,TCP协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,因为TCP是面向流的协议,是可靠传输的,但是会有粘包的现象; ?????????而UDP一次收发数据大小是有限制(65507B)的,不能一次提取任意字节的数据,这一点和TCP是很不同的,因为UDP传输是面向报文的,udp本身就是不可靠协议,用数据包(消息来源地址,端口等信息)的形式发送,底层会把数据分成一段一段发过去(最大1500B,网卡的MTU限制),一旦被拆包后,有的数据会被分成没有报头信息,所以就会发生丢包现象。?? ? ? ? ? ?为了UDP在传输文件的过程中,另一端收到的文件是完整的、有效的;所以让UDP通过建立传输收发确认机制,因为UDP服务端收到客户端发来的第一次数据后,还要进行写入文件,过程还要花点时间,客户端可能已经发送第二次数据过去了,服务端还没来的及接收第二次数据,这样就会有大量的数据丢失,所以收发一次就应答一次,保证了每一次发送和接收的数据都能达到。UDP通过这种机制来传输文件,文件传输的速度也受到影响,UDP和TCP传输速度相比,都是差不多。
?????????子进程有事务,需要先放到队列里,由 UI主进程里的 子线程去取出事务,子线程再通过 signal 通知 UI 主线程处理。
????????父进程让子进程结束。
???????使用multiprocessing的队列进程间进行通信。
? ? ? ? 考虑有些情况下,发送端发送的文件名过于太长,超过给定固定的文件名长度(60个Byte长度),文件头部信息解析就有问题(收发文件都是转用Byte类型),因为中文encode()的长度和len(str)的长度不一样
????????所以发送文件头部信息前,先判断文件名是否含有中文,含用中文的情况,以下代码实现
?运行程序?????????前提准备:电脑有安装python3.9+,支持Windows/Linux ?????????依赖库:pip install PySide2 ?????????同时可以多开几个终端,运行脚本 ?????????运行脚本:python ui_main.py 文件传输效果图????????TCP协议传输????????UDP协议传输?具体代码????????链接:https://pan.baidu.com/s/1SH9UBA2rEkKuuZRa6TMM9Q? 今天就先到这里啦,如果对你有帮助的,赶紧收藏下来吧!觉得代码哪里有问题或者有建议的,都可以打在评论上,我会留意的,互相学习,互相探讨,你我皆黑马。 后续我将继续新增FTP协议... |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 20:24:44- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |