基于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_())
关键修改说明
- 线程化Socket接收:把阻塞的消息接收逻辑放到
QThread中,彻底避免卡死GUI - 信号槽传递消息:用Qt的
pyqtSignal实现线程和UI之间的安全通信(Qt禁止在非主线程操作UI) - 修复Socket对象错误:服务器端用
accept()返回的conn进行通信,而不是监听用的server_socket - 绑定按钮事件:给发送按钮绑定
send函数,点击即可发送消息 - 新增消息显示区:添加只读文本框,直观显示对方发送的消息
- 完善导入:补上所有必要的模块,保证代码能正常运行
使用步骤
- 先运行服务器端代码,控制台会显示「等待客户端连接...」
- 再运行客户端代码,连接成功后就能互相发送消息了
- 输入消息后点击「Send」按钮,对方的接收框会实时显示消息
内容的提问来源于stack exchange,提问作者Dávid Nagy
相关产品推荐
相关产品推荐

