python socket编程8 - PyQt6界面实现TCP server/client 多客户端通讯的例子

一、小总结:

本篇在单机通讯的基础上进行重构,实现多线程TCP server与多个TCP client通讯的例子。

创建两个 PyQt6的项目,一个作为TCP server 项目,另一个作为TCP client项目。

二、TCP server部分

1、TCPServer 类定义中增加数组模拟线程池

2、消息发送修改为遍历线程池

3、停止服务修改为遍历线程池

4、TCP server完整代码

复制代码
import socket

from PyQt6.QtCore import QThread, pyqtSignal


class TCPServer:

    def __init__(self, ui, server_ip, server_hostname, server_port):

        self.ui = ui  # 主界面
        self.ip = server_ip  # 服务器ip地址
        self.port = server_port  # 服务器端口号
        self.serverName = server_hostname  # 显示名称
        self.is_running = False  # 是否已经启动

        self.socket = None  # socket
        # self.socketThread = None  # 新的 socket receive 线程
        self.connectedThreadPool = []  # 模拟线程池,替代上面的 socket receive 线程
        self.ui.statusbar.showMessage("服务已经启动,等待客户端的连接......")
        self.start()

    def start(self):
        if not self.is_running:
            self.is_running = True
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.bind((self.ip, self.port))  # 绑定IP与端口
            self.socket.listen(10)  # 设定最大连接数
            self.startSocketReceiveThread()

    def stop(self):
        try:
            if self.is_running:
                self.is_running = False
                for connectThread in self.connectedThreadPool:
                    if connectThread.is_running:
                        connectThread.stop()
        except Exception as e:
            print(e)

    def startSocketReceiveThread(self):
        """
        启动一个新的监听线程,等待连接。
        :return:
        """
        socketThread = TCPServerSocketReceiveThread(self.socket)
        self.connectedThreadPool.append(socketThread)
        socketThread.clientConnection.connect(self.socket_client_connect_trigger)
        socketThread.receivedClientData.connect(self.show_client_message)
        socketThread.serverStatus.connect(self.server_status_trigger)
        socketThread.start()

    def server_status_trigger(self, status):
        self.ui.statusbar.showMessage(status)

    def socket_client_connect_trigger(self, state):
        if state == 'connect':
            self.startSocketReceiveThread()
        else:
            self.ui.statusbar.showMessage("客户端已经断开。")

    def show_client_message(self, message):
        self.ui.textEdit.append(message)

    def send_message_to_client(self, message):  # 改为广播
        if self.is_running:
            message = self.serverName + ':' + message
            self.ui.textEdit.append(message)
            for connectThread in self.connectedThreadPool:
                if connectThread.is_connected:
                    connectThread.send_data_to_client(message)


class TCPServerSocketReceiveThread(QThread):
    clientConnection: pyqtSignal = pyqtSignal(str)  # 向主线程发送连接状态标志
    receivedClientData: pyqtSignal = pyqtSignal(str)  # 向主线程发送接受到客户端的数据
    serverStatus: pyqtSignal = pyqtSignal(str)  # 向主线程发送服务器状态

    def __init__(self, serverSocket):
        super(TCPServerSocketReceiveThread, self).__init__()
        self.serverSocket = serverSocket
        self.clientSocket = None
        self.addr = None
        self.clientIP = None
        self.clientPort = None
        self.is_running = True
        self.is_connected = False

    def run(self):
        self.clientSocket, self.addr = self.serverSocket.accept()  # 接受客户端的连接
        self.is_connected = True
        self.emitConnectEvent('connect')  # 发送客户端连接成功通知到主界面
        self.clientIP, self.clientPort = self.addr
        self.serverStatus.emit("客户端【" + self.clientIP + "】已经连接。")
        self.startReceiveData()

    def startReceiveData(self):

        while self.is_running:
            try:
                data = self.clientSocket.recv(1024).decode('utf-8')  # 接受到字符串并按照utf-8编译
                if not data:
                    self.emitConnectEvent('disconnect')  # 发送客户端断开通知到主界面
                    break
                print(data)
                self.sendClientDataToUi(data)
            except ConnectionResetError as reason:
                self.sendClientDataToUi("已经离开对话。")
                self.is_running = False
                self.emitConnectEvent('disconnect')  # 发送客户端断开通知到主界面
                break
        self.clientSocket.close()
        # self.serverSocket.close()
        # self.serverStatus.emit("服务已经关闭。")

    def send_data_to_client(self, message):
        try:
            self.clientSocket.send(message.encode("utf-8"))
        except Exception as reason:
            print("发送失败,原因:", reason)

    def stop(self):
        if self.is_running:
            self.is_running = False

    def emitConnectEvent(self, state):
        self.clientConnection.emit(state)

    def sendClientDataToUi(self, message):
        self.receivedClientData.emit(message)

三、TCP client 完整代码

复制代码
import socket
from PyQt6.QtCore import QThread, pyqtSignal


class TCPClient:

    def __init__(self, ui, ip, clientName, port):
        self.ui = ui
        self.ip = ip
        self.hostName = clientName
        self.port = port

        self.socket = None
        self.socketThread = None
        self.connect_server()

    def connect_server(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socketThread = TCPClientSocketReceiveThread(self.socket)
        self.socketThread.receivedServerData.connect(self.update_ui_chat_content)
        if self.connect_success(self.ip, self.port):
            self.socketThread.start()

    def update_ui_chat_content(self, serverMessage):
        self.ui.textEdit.append(serverMessage)

    def stop(self):
        self.socketThread.stop()

    def send_data(self, sentence):
        sentence = self.hostName + ":" + sentence
        self.ui.textEdit.append(sentence)
        self.socket.send(sentence.encode())

    def connect_success(self, ip, port):
        try:
            self.socket.connect((ip, port))
            return True
        except Exception as reason:
            print(reason)
            return False


class TCPClientSocketReceiveThread(QThread):
    receivedServerData: pyqtSignal = pyqtSignal(str)  # 向主线程发送接受到客户端的数据

    def __init__(self, clientSocket):
        super(TCPClientSocketReceiveThread, self).__init__()
        self.clientSocket = clientSocket
        self.is_running = True

    def stop(self):
        self.is_running = False
        self.clientSocket.close()

    def run(self):
        while self.is_running:
            try:
                msg = self.clientSocket.recv(1024).decode("utf-8")  # 接受服务端消息
                if not msg:
                    break
                self.receivedServerData.emit(msg)
            except Exception as reason:
                print(reason)
                break
        self.stop()
        self.receivedServerData.emit("已经与服务端断开。")

四、截图

1、server端

2、第一个客户端

3、第二个客户端

可以单机多个客户端通讯,也可以在局域网测试。

相关推荐
景彡先生1 小时前
Python requests详解:从入门到实战,HTTP请求的“瑞士军刀”
python
深度学习lover1 小时前
<数据集>yolo螺丝螺母识别数据集<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·螺丝螺母识别
Geoking.1 小时前
PyTorch 基础详解:tensor.item() 方法
人工智能·pytorch·python
ZIM学编程1 小时前
「学长有话说」作为一个大三学长,我想对大一计算机专业学生说这些!
java·c语言·数据结构·c++·python·学习·php
没有钱的钱仔2 小时前
conda 基础命令使用
python
程序员三藏2 小时前
如何使用Selenium做自动化测试?
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
Python私教2 小时前
基于 Django 5 + DRF 构建博客系统后端接口(从建模到接口实现)
python·django·sqlite
南方的狮子先生2 小时前
【深度学习】60 分钟 PyTorch 极速入门:从 Tensor 到 CIFAR-10 分类
人工智能·pytorch·python·深度学习·算法·分类·1024程序员节
闲人编程3 小时前
Docker化你的Python应用:从开发到生产
python·docker·eureka·开发·生产·codecapsule
JJJJ_iii3 小时前
【机器学习10】项目生命周期、偏斜类别评估、决策树
人工智能·python·深度学习·算法·决策树·机器学习