赞
踩
首先,在 《python socket编程5 - 最简单的命令行启动的tcp/udp server/client例子》 中实现了最简单的TCP/UDP server和client在命令行下可执行的代码。
然后,分别在《python socket编程6 - 使用PyQt6 开发UI界面实现TCP server和TCP client单机通讯的例子》和《python socket编程7 - 使用PyQt6 开发UI界面新增实现UDP server和client单机通讯的例子》中使用PyQt6做了一个界面,实现客户端和服务端TCP/UDP的单机通讯。
本篇在单机通讯的基础上进行重构,实现多线程TCP server与多个TCP client通讯的例子。
创建两个 PyQt6的项目,一个作为TCP server 项目,另一个作为TCP client项目。
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)
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("已经与服务端断开。")
可以单机多个客户端通讯,也可以在局域网测试。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。