WebSocket双向消息异常:ChatB消息无法在ChatA端显示求助
问题分析与修复方案
核心问题原因
- UI元素复用错误:ChatB的发送逻辑错误地将发送消息添加到接收列表(
#received-messages),而非自身发送列表,造成"自己收到自己消息"的视觉错觉。 - 聊天窗口未分离:当前代码中ChatA、ChatB共用同一接收列表,导致消息显示混乱,无法区分消息归属。
- 服务端连接存储逻辑可优化(非直接故障点,但影响扩展性)。
修复步骤
1. 分离HTML中的聊天区域
确保ChatA和ChatB拥有独立的发送/接收UI元素:
<!-- ChatA 专属区域 --> <div id="chat-a"> <h3>ChatA</h3> <form id="form-send"> <input type="text" id="input-send" placeholder="输入消息"> <button type="submit">发送</button> </form> <ul id="chat-a-sent">发送的消息:</ul> <ul id="chat-a-received">收到的消息:</ul> </div> <!-- ChatB 专属区域 --> <div id="chat-b"> <h3>ChatB</h3> <form id="form-receive"> <input type="text" id="input-receive" placeholder="输入消息"> <button type="submit">发送</button> </form> <ul id="chat-b-sent">发送的消息:</ul> <ul id="chat-b-received">收到的消息:</ul> </div>
2. 修改客户端代码,绑定对应UI元素
ChatA 代码调整
$('#form-send').submit(function(e){ e.preventDefault(); if (fileToSend) { var reader = new FileReader(); reader.onload = function(e) { var contents = e.target.result; console.log('Sending file from ChatA: ', fileToSend.name); socketA.send(contents); $('#chat-a-sent').append($('<li>').text('Sent: ' + fileToSend.name)); fileToSend = null; }; reader.readAsDataURL(fileToSend); } else { var message = 'ChatA:' + $('#input-send').val(); console.log('Sending message from ChatA: ', message); socketA.send(message); $('#chat-a-sent').append($('<li>').text('Sent: ' + message)); $('#input-send').val(''); } }); socketA.onmessage = function(event){ var msg = event.data; console.log('Received message at ChatA: ', msg); if (msg.startsWith('File received:')) { var filename = msg.split(': ')[1]; $('#chat-a-received').append($('<li>').html('Received: <a href="' + filename + '" download>' + filename + '</a>')); } else { $('#chat-a-received').append($('<li>').text('Received: ' + msg)); } };
ChatB 代码调整(重点修复发送消息的UI绑定)
$('#form-receive').submit(function(e){ e.preventDefault(); var message = 'ChatB:' + $('#input-receive').val(); console.log('Sending message from ChatB: ', message); socketB.send(message); // 修复:将发送消息添加到ChatB自身的发送列表 $('#chat-b-sent').append($('<li>').text('Sent: ' + message)); $('#input-receive').val(''); }); socketB.onmessage = function(event){ var msg = event.data; console.log('Received message at ChatB: ', msg); if (msg.startsWith('File received:')) { var filename = msg.split(': ')[1]; $('#chat-b-received').append($('<li>').html('Received: <a href="' + filename + '" download>' + filename + '</a>')); } else { $('#chat-b-received').append($('<li>').text('Received: ' + msg)); } };
3. (可选)优化服务端连接逻辑(提升扩展性)
将服务端按客户端类型存储连接,避免依赖websocket对象ID:
from starlette.applications import Starlette from starlette.responses import HTMLResponse from starlette.websockets import WebSocketDisconnect from starlette.routing import Route, WebSocketRoute, Mount from starlette.staticfiles import StaticFiles import base64 # 按客户端类型存储连接 connected_websockets = {'ChatA': [], 'ChatB': []} async def websocket_endpoint(websocket): await websocket.accept() # 客户端连接后先发送身份标识(ChatA/ChatB) client_type = await websocket.receive_text() if client_type in connected_websockets: connected_websockets[client_type].append(websocket) try: while True: data = await websocket.receive_text() if data.startswith('ChatA:'): data = data[6:] # 给所有ChatB客户端推送消息 for ws in connected_websockets['ChatB']: await ws.send_text(f"Received from ChatA: {data}") elif data.startswith('ChatB:'): data = data[6:] # 给所有ChatA客户端推送消息 for ws in connected_websockets['ChatA']: await ws.send_text(f"Received from ChatB: {data}") elif data.startswith('data:'): await handle_file(data, websocket) except WebSocketDisconnect: if client_type in connected_websockets: connected_websockets[client_type].remove(websocket) async def handle_file(data_url, websocket): file_type, data = data_url.split(';')[0].split('/')[1], data_url.split(',')[1] file_data = base64.b64decode(data) with open('received.' + file_type, 'wb') as f: f.write(file_data) await websocket.send_text(f"File received: received.{file_type}") routes = [ WebSocketRoute("/ws", websocket_endpoint), Mount("/", StaticFiles(directory="static"), name="static") ] app = Starlette(routes=routes)
对应客户端需在连接成功后发送身份标识:
// ChatA 连接时发送身份 var socketA = new WebSocket('ws://localhost:8001/ws'); socketA.onopen = function() { socketA.send('ChatA'); }; // ChatB 连接时发送身份 var socketB = new WebSocket('ws://localhost:8001/ws'); socketB.onopen = function() { socketB.send('ChatB'); };
验证方法
- 打开浏览器控制台,查看ChatA、ChatB的日志输出,确认消息发送/接收流程是否正常。
- 发送消息后检查各自的发送/接收列表,确认消息归属正确。
内容的提问来源于stack exchange,提问作者Alex Doc
相关产品推荐
相关产品推荐

