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

单客户端多WebSocket连接问题:标签页输出异常排查与解决

问题分析与解决方案

问题根源

所有输出都显示在最后一个标签页的问题,不是因为WebSocket局部变量被覆盖,而是outputElement未用let/const声明,自动成为全局变量。每次调用openWebSocket时,都会覆盖这个全局变量,导致所有WebSocket的onmessage回调最终都指向最后创建的标签页输出元素。

解决步骤

1. 修复变量作用域问题

在openWebSocket函数中给outputElement添加const声明,让它成为函数局部变量,确保每个WebSocket回调绑定自己对应的输出元素:

function openWebSocket(outputId, ws_message) {
    const outputElement = document.getElementById(outputId); // 声明为局部变量
    const ws = new WebSocket("ws://" + window.location.host + "/ws");

    ws.onopen = function() {
        ws.send(ws_message); // 向服务端发送启动命令的消息
    };

    ws.onmessage = function(event) {
        outputElement.innerText += event.data + '\n';
    };
}

2. 完善标签页动态创建逻辑

原代码缺失标签和输出区域的创建逻辑,补充后确保每个标签页有唯一ID:

let tabCounter = 1;

function createNewTab(ws_message) {
    const tabId = `tab-${tabCounter}`;
    const outputId = `output-${tabCounter}`;

    // 创建标签项
    const tabItem = document.createElement('li');
    tabItem.className = 'nav-item';
    tabItem.setAttribute('role', 'presentation');
    
    const tabLink = document.createElement('a');
    tabLink.className = 'nav-link';
    tabLink.id = `${tabId}-tab`;
    tabLink.setAttribute('data-bs-toggle', 'tab');
    tabLink.setAttribute('href', `#${tabId}`);
    tabLink.setAttribute('role', 'tab');
    tabLink.setAttribute('aria-controls', tabId);
    tabLink.innerText = `命令 ${tabCounter}`;
    
    tabItem.appendChild(tabLink);
    document.getElementById('commandTabs').appendChild(tabItem);

    // 创建标签内容区域
    const tabContent = document.createElement('div');
    tabContent.className = 'tab-pane fade';
    tabContent.id = tabId;
    tabContent.setAttribute('role', 'tabpanel');
    tabContent.setAttribute('aria-labelledby', `${tabId}-tab`);
    
    const outputArea = document.createElement('pre');
    outputArea.id = outputId;
    outputArea.style.whiteSpace = 'pre-wrap';
    outputArea.style.height = '300px';
    outputArea.style.overflowY = 'auto';
    
    tabContent.appendChild(outputArea);
    document.getElementById('commandTabContent').appendChild(tabContent);

    // 激活新标签页
    tabLink.classList.add('active');
    tabContent.classList.add('show', 'active');

    tabCounter++;
    openWebSocket(outputId, ws_message);
}

3. 关于WebSocket实例的存储

不需要全局数组存储WebSocket实例,只要变量作用域正确,每个ws实例会被对应回调引用,不会被垃圾回收。如果后续需要主动关闭或批量管理连接,可以用全局对象存储:

const wsConnections = {}; // 全局对象存储连接

function openWebSocket(outputId, ws_message) {
    const outputElement = document.getElementById(outputId);
    const ws = new WebSocket("ws://" + window.location.host + "/ws");
    
    wsConnections[outputId] = ws; // 存入全局对象
    
    ws.onopen = function() {
        ws.send(ws_message);
    };

    ws.onmessage = function(event) {
        outputElement.innerText += event.data + '\n';
    };

    ws.onclose = function() {
        delete wsConnections[outputId]; // 关闭后移除记录
    };
}

额外提示

  • 可给标签页添加关闭按钮,点击时关闭对应WebSocket连接并移除标签和内容区域。
  • 服务端需区分不同WebSocket连接,确保每个连接返回对应命令的输出,而非广播所有输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 16:46:16