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、第二个客户端

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

相关推荐
炘东5921 小时前
求助:在另一个 Reasonix 窗口或后台运行
python
apcipot_rain1 小时前
计科八股2026705——正态分布、独立与不相关、范数、列表元组集合、链表队列、sort函数、URL执行
python·计算机网络·概率论
江华森1 小时前
Python 编程快速上手:让繁琐工作自动化 —— 19章全实战笔记
笔记·python·自动化
子豪-中国机器人1 小时前
第一部分:初赛选择题(30 题,对应线上每日问答)
python
Sam09271 小时前
【AI 算法精讲 15】余弦相似度:向量检索与归一化内积
人工智能·python·算法·ai
Starry-sky(jing)2 小时前
RecursionError: maximum recursion depth exceeded —— 你的函数调用链,踩穿了 CPython 的安全气囊
python·cpython·pydantic·recursionerror·递归深度·递归限制·sys.setrecursionlimit
cui_ruicheng2 小时前
Python从入门到实战(三):流程控制与循环语句
开发语言·python
lihao lihao2 小时前
Tcp Socket
网络·网络协议·tcp/ip
Rauser Mack2 小时前
Vibe coding游戏实战:零代码编程五子棋小游戏
人工智能·python·游戏·html·prompt
FriendshipT2 小时前
Ultralytics:解读PSABlock模块
人工智能·pytorch·python·深度学习·目标检测