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

WebSocket双向消息异常:ChatB消息无法在ChatA端显示求助

问题分析与修复方案

核心问题原因

  1. UI元素复用错误:ChatB的发送逻辑错误地将发送消息添加到接收列表(#received-messages),而非自身发送列表,造成"自己收到自己消息"的视觉错觉。
  2. 聊天窗口未分离:当前代码中ChatA、ChatB共用同一接收列表,导致消息显示混乱,无法区分消息归属。
  3. 服务端连接存储逻辑可优化(非直接故障点,但影响扩展性)。

修复步骤

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');
};

验证方法

  1. 打开浏览器控制台,查看ChatA、ChatB的日志输出,确认消息发送/接收流程是否正常。
  2. 发送消息后检查各自的发送/接收列表,确认消息归属正确。

内容的提问来源于stack exchange,提问作者Alex Doc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:24:58