You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PyQt实现一对一Server-Client连接的简易程序开发咨询

Hey,我瞅了下你写的代码,发现几个关键问题导致GUI和Socket通信没法正常跑起来——最主要的是阻塞的while循环直接卡死了Qt的事件循环,还有一些小的导入和逻辑错误。我帮你调整了代码,解决这些问题,实现真正的双向通信功能:


核心问题分析

  • Qt的GUI是单线程事件循环驱动的,你原来的while True循环会阻塞主线程,导致界面完全无响应
  • 服务器类里错误地把监听用的server_socket传给了Dialog,实际应该用accept()返回的连接对象conn(这才是和客户端通信的Socket)
  • 缺少必要的模块导入(比如socket、QDialog)
  • 发送按钮的点击信号没有和send函数绑定,点了等于白点
  • 接收消息的逻辑必须放在后台线程里,不然会阻塞UI操作

修复后的完整代码

服务器端代码

import socket
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QDialog, QApplication
import sys

# Designer生成的UI类(新增了接收消息显示框)
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(353, 266)
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 30, 47, 13))
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(80, 110, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setGeometry(QtCore.QRect(80, 20, 141, 71))
        self.textEdit.setObjectName("textEdit")
        # 新增只读文本框,用来显示接收的消息
        self.receiveTextEdit = QtWidgets.QTextEdit(Dialog)
        self.receiveTextEdit.setGeometry(QtCore.QRect(80, 150, 141, 71))
        self.receiveTextEdit.setReadOnly(True)
        self.receiveTextEdit.setObjectName("receiveTextEdit")
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "服务器端"))
        self.label.setText(_translate("Dialog", "Message:"))
        self.pushButton.setText(_translate("Dialog", "Send"))
        self.receiveTextEdit.setPlaceholderText(_translate("Dialog", "接收的消息显示在这里"))

# 自定义Dialog,处理UI逻辑
class ServerDialog(QDialog, Ui_Dialog):
    # 定义信号,用于从后台线程接收消息
    receive_signal = QtCore.pyqtSignal(str)

    def __init__(self, conn):
        super(ServerDialog, self).__init__()
        self.setupUi(self)
        self.conn = conn
        # 绑定发送按钮的点击事件
        self.pushButton.clicked.connect(self.send)
        # 绑定接收信号到UI更新函数
        self.receive_signal.connect(self.update_receive_text)

    def send(self):
        message = self.textEdit.toPlainText().strip()
        if message:
            self.conn.send(message.encode())
            self.textEdit.clear()  # 发送后清空输入框

    def update_receive_text(self, message):
        self.receiveTextEdit.append(f"客户端: {message}")

# 后台线程类,处理Socket接收消息(避免阻塞UI)
class ServerReceiveThread(QtCore.QThread):
    message_received = QtCore.pyqtSignal(str)

    def __init__(self, conn):
        super(ServerReceiveThread, self).__init__()
        self.conn = conn

    def run(self):
        while True:
            try:
                data = self.conn.recv(1024).decode()
                if not data:
                    break
                self.message_received.emit(data)  # 发送信号给UI线程
            except Exception as e:
                print(f"连接断开: {e}")
                break
        self.conn.close()

# 服务器主逻辑
class Server():
    def __init__(self, app):
        host = socket.gethostname()
        port = 5000
        server_socket = socket.socket()
        server_socket.bind((host, port))
        server_socket.listen(1)
        print("等待客户端连接...")
        conn, address = server_socket.accept()
        print("连接来自: " + str(address))
        
        # 创建GUI并启动接收线程
        self.gui = ServerDialog(conn)
        self.gui.show()
        
        self.receive_thread = ServerReceiveThread(conn)
        self.receive_thread.message_received.connect(self.gui.receive_signal.emit)
        self.receive_thread.start()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    server = Server(app)
    sys.exit(app.exec_())

客户端代码

import socket
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QDialog, QApplication
import sys

# Designer生成的UI类(和服务器端结构一致,仅窗口标题不同)
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(353, 266)
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 30, 47, 13))
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(80, 110, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setGeometry(QtCore.QRect(80, 20, 141, 71))
        self.textEdit.setObjectName("textEdit")
        self.receiveTextEdit = QtWidgets.QTextEdit(Dialog)
        self.receiveTextEdit.setGeometry(QtCore.QRect(80, 150, 141, 71))
        self.receiveTextEdit.setReadOnly(True)
        self.receiveTextEdit.setObjectName("receiveTextEdit")
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "客户端"))
        self.label.setText(_translate("Dialog", "Message:"))
        self.pushButton.setText(_translate("Dialog", "Send"))
        self.receiveTextEdit.setPlaceholderText(_translate("Dialog", "接收的消息显示在这里"))

# 自定义Dialog
class ClientDialog(QDialog, Ui_Dialog):
    receive_signal = QtCore.pyqtSignal(str)

    def __init__(self, client_socket):
        super(ClientDialog, self).__init__()
        self.setupUi(self)
        self.client_socket = client_socket
        self.pushButton.clicked.connect(self.send)
        self.receive_signal.connect(self.update_receive_text)

    def send(self):
        message = self.textEdit.toPlainText().strip()
        if message:
            self.client_socket.send(message.encode())
            self.textEdit.clear()

    def update_receive_text(self, message):
        self.receiveTextEdit.append(f"服务器: {message}")

# 后台接收线程
class ClientReceiveThread(QtCore.QThread):
    message_received = QtCore.pyqtSignal(str)

    def __init__(self, client_socket):
        super(ClientReceiveThread, self).__init__()
        self.client_socket = client_socket

    def run(self):
        while True:
            try:
                data = self.client_socket.recv(1024).decode()
                if not data:
                    break
                self.message_received.emit(data)
            except Exception as e:
                print(f"连接断开: {e}")
                break
        self.client_socket.close()

# 客户端主逻辑
class Client():
    def __init__(self, app):
        host = socket.gethostname()
        port = 5000
        client_socket = socket.socket()
        client_socket.connect((host, port))
        print("已连接到服务器")
        
        self.gui = ClientDialog(client_socket)
        self.gui.show()
        
        self.receive_thread = ClientReceiveThread(client_socket)
        self.receive_thread.message_received.connect(self.gui.receive_signal.emit)
        self.receive_thread.start()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    client = Client(app)
    sys.exit(app.exec_())

关键修改说明

  1. 线程化Socket接收:把阻塞的消息接收逻辑放到QThread中,彻底避免卡死GUI
  2. 信号槽传递消息:用Qt的pyqtSignal实现线程和UI之间的安全通信(Qt禁止在非主线程操作UI)
  3. 修复Socket对象错误:服务器端用accept()返回的conn进行通信,而不是监听用的server_socket
  4. 绑定按钮事件:给发送按钮绑定send函数,点击即可发送消息
  5. 新增消息显示区:添加只读文本框,直观显示对方发送的消息
  6. 完善导入:补上所有必要的模块,保证代码能正常运行

使用步骤

  1. 先运行服务器端代码,控制台会显示「等待客户端连接...」
  2. 再运行客户端代码,连接成功后就能互相发送消息了
  3. 输入消息后点击「Send」按钮,对方的接收框会实时显示消息

内容的提问来源于stack exchange,提问作者Dávid Nagy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:58:15