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

一、小总结:

  • 首先,在 《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项目。

二、TCP server部分

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

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

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

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

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

python socket编程8 - PyQt6界面实现TCP server/client 多客户端通讯的例子_第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端

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

2、第一个客户端

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

3、第二个客户端

python socket编程8 - PyQt6界面实现TCP server/client 多客户端通讯的例子_第6张图片
可以单机多个客户端通讯,也可以在局域网测试。

你可能感兴趣的:(python,socket编程,python,PyQt6,python,tcp/ip,socket)