使用pyqt的QThread实现多线程的QTcpServer

本文章采用与《使用pyqt的QUdpSocket传文本信息、用QTcpServer传文件的样例》(该文章是从C++上的Qt收发文件的代码翻译过来的,个人认为过于复杂不易理解)不同的写法实现TCP收发文件,并增加多线程的功能。本文章的写法更接近于socketserver.ThreadingUDPServer、socketserver.ThreadingTCPServer的写法,可参考《使用socketserver的ThreadingUDPServer、ThreadingTCPServer实现收发消息和文件》
一共2个py文件:tcpClient.py、tcpServer.py

1.tcpClient.py只实现发送文件功能
2.tcpServer.py只实现接收文件功能,且有单线程和多线程两种方式
3.tcpClient.py+tcpServer.py(单线程)搭配使用,还提供了接收文件前的弹出窗口确认是否接收功能
4.tcpClient.py+tcpServer.py(多线程)搭配使用,无法实现接收文件前的弹出窗口确认是否接收功能,因为所有的QtWidgets部件,只能在QApplication主线程创建和使用,Qt不允许从子线程直接更新主线程里的QtWidgets部件。虽然可以自定义信号连接到自定义的槽函数调用QMessageBox.question(),但信号emit()后无法获取isReadyToRecvFile()返回值。
5.QThread简介: QThread类提供了一个与平台无关的管理线程的方法。
QThread的执行从run()函数的执行开始,run()函数退出,意味着线程的终止
run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。
QThread的用法:继承QThread类,重载Thread中的run()函数,调用start()函数来启动线程
6.还可以使用QThreadPool来管理QThread,本文不涉及,请自行研究。

客户端tcpClient.py代码:

# coding=utf-8
import os
import sys
import json
from PyQt6.QtCore import QFile
from PyQt6.QtWidgets import *
from PyQt6.QtNetwork import QHostAddress, QNetworkInterface, QAbstractSocket, QTcpSocket


class ClientWidget(QWidget):

    buffsize = 32 * 1024

    def __init__(self):
        super(ClientWidget, self).__init__()
        self.resize(500, 450)
        self.setWindowTitle('Client')
        self.progressbar = QProgressBar(self)
        self.browser = QTextBrowser(self)
        bt_sendFile = QPushButton('发送文件')
        bt_sendFile.clicked.connect(self.sendFile)
        layout = QVBoxLayout()
        layout.addWidget(self.progressbar)
        layout.addWidget(self.browser)
        layout.addWidget(bt_sendFile)
        self.setLayout(layout)

        self.localIP = self.getLocalIP()
        self.initTcpClient()

    def getLocalIP(self):
        '''获取本地主机IP'''
        for addr in QNetworkInterface.allAddresses():
            # 只要IPv4地址,过滤掉127.0.0.1
            if addr.protocol() == QAbstractSocket.NetworkLayerProtocol.IPv4Protocol and addr != QHostAddress.SpecialAddress.LocalHost:
                address = addr.toString()
                # 169开头的IP地址表示本地主机未从DHCP分配到有效IP,过滤网关地址x.x.x.1
                if address[:3] != '169' and address.split('.')[-1] != '1':
                    return address
        return '0.0.0.0'

    def initTcpClient(self):
        self.tcpSocket = QTcpSocket()
        self.tcpSocket.connected.connect(self.onTcpClientConnected)
        self.tcpSocket.disconnected.connect(self.onDisconnected)

    def onTcpClientConnected(self):
        peer_address = self.tcpSocket.peerAddress().toString()
        peer_port = self.tcpSocket.peerPort()
        news = 'Connected with address {}, port {}'.format(peer_address, str(peer_port))
        self.browser.append(news)

        filePath = self.tcpSocket.dt['msg']
        self.tcpSocket.dt['msg'] = os.path.basename(filePath)  # 只传文件名
        self.tcpSocket.dt['size'] = os.stat(filePath)[6]  # 文件大小(字节)
        dt_encode = json.dumps(self.tcpSocket.dt).encode('utf-8')
        self.tcpSocket.write(dt_encode)
        if self.tcpSocket.waitForReadyRead():  # 等待对方回复是否同意接收文件
            data = self.tcpSocket.readAll()
            if data == b'ready':
                fromFile = QFile(filePath)
                if not fromFile.open(QFile.OpenModeFlag.ReadOnly):
                    self.browser.append('无法打开要发送的文件:%s' % filePath)
                    self.tcpSocket.close()
                    
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值