```html Python socket CS 聊天 + PyQt5 Python socket CS 聊天 + PyQt5 用原生 socket 搭骨架,PyQt5 套皮,聊天一个能跑起来的聊天局域网聊天工具就这样出来了。 写聊天室最烦的聊天是什么?不是 socket 本身,是聊天界面。命令行里 recv 和 input 来回切,聊天消息一多就糊成一团。聊天PyQt5 的聊天 QThread 把收发放到后台,主线程只管刷 QTextEdit,聊天体验直接上一个台阶。聊天下面这套代码,聊天服务端和客户端都跑在本地,聊天改个 IP 就能在局域网用。聊天 服务端用 socket.AF_INET + SOCK_STREAM,聊天经典 TCP。聊天每个客户端连接进来就开一个线程,把 conn 塞进全局列表 clients 里。广播函数 broadcast(msg, _exclude=None) 遍历列表逐个 send,谁掉线就 remove 掉,别让死连接拖垮整个循环。有个细节:send 要加 try/except,不然客户端一关,服务端直接崩给你看。 # 服务端核心片段 import socket, threading clients = [] def broadcast(msg, _exclude=None): for c in clients[:]: if c != _exclude: try: c.send(msg.encode('utf-8')) except: clients.remove(c) def handle(conn, addr): while True: try: data = conn.recv(1024) if not data: break broadcast(f"[{ addr[1]}] { data.decode()}", conn) except: break clients.remove(conn) conn.close() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', 8888)) server.listen(5) while True: conn, addr = server.accept() clients.append(conn) threading.Thread(target=handle, args=(conn, addr), daemon=True).start() 客户端这边,PyQt5 的窗口大概长这样:上面一个 QTextBrowser 显示聊天记录,下面一个 QLineEdit 加 QPushButton 发消息。关键是把 socket 收消息放到 QThread 里,通过 pyqtSignal 把字符串丢回主线程更新 UI。直接在主线程 recv 会卡死窗口,鼠标转圈那种,别问我怎么知道的。 # 客户端接收线程 from PyQt5.QtCore import QThread, pyqtSignal class RecvThread(QThread): msg_signal = pyqtSignal(str) def __init__(self, sock): super().__init__() self.sock = sock def run(self): while True: try: data = self.sock.recv(1024) if data: self.msg_signal.emit(data.decode('utf-8')) else: break except: break 主窗口里把 msg_signal 连到 append 方法,收到啥就往 QTextBrowser 里追加一行。发送按钮的 clicked 信号绑一个 send_msg,里面 self.sock.send(text.encode()),顺手清空输入框。注意编码统一用 utf-8,不然中文变问号,调试半天以为是 socket 的锅。 有个坑得提一嘴:PyQt5 的 QThread 和 Python 原生 threading 别混着用。服务端用 threading 没问题,客户端收消息一定用 QThread,因为要发信号。信号跨线程是 Qt 的强项,pyqtSignal 默认队列连接,安全得很。要是图省事在 threading.Thread 里直接调 ui.textBrowser.append,程序大概率随机崩溃,还找不到原因。 界面美化不用太花哨,QSS 调一下背景色和圆角就够了。比如 QTextBrowser { background: #f0f0f0; border: 1px solid #ccc; border-radius: 6px; padding: 8px; },输入框加个 placeholder 写“输入消息...”,按钮 hover 变个色。这些细节花不了十分钟,但看起来不像半成品。 最后说下测试。开两个客户端,一个发“你好”,另一个秒收。服务端控制台打印 [端口号] 你好,说明广播正常。关掉一个客户端,另一个继续发,服务端不报错,clients 列表自动清理。整个过程没有用任何第三方网络库,纯 socket 加 PyQt5,依赖干净,打包成 exe 也就 30MB 左右。 这套东西写下来不到 200 行,但把 TCP 粘包、线程安全、UI 刷新这几个核心问题都摸了一遍。想加功能也简单:用户名在连接时发个特殊前缀,服务端解析后存字典;私聊就遍历 clients 找对应 conn 单独 send。底层通了,上层随便折腾。 ``` |