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

使用原生WebSocket连接SignalR Hub无法接收消息,SignalR JS库正常

原生WebSocket连接SignalR Hub无法接收消息的问题

场景说明

我需要实现客户端与SignalR Hub的连接,但无法直接使用SignalR JS库(该库无法通过Rollup构建)。测试时,我用C#线程每10秒向所有客户端发送消息:

Thread cyclicThread = ThreadUtils.GetCyclicThread(10000, () =>
{
    this.NotificationHub.Clients.All.SendAsync("methodeTestBis", 239);
});
cyclicThread.Start();

使用SignalR JS库时可正常接收消息,Chrome调试器网络面板可见消息:

// 使用SignalR JS库测试,可正常接收消息
const connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

connection.start();

使用SignalR JS库时的网络面板:可见消息接收记录

但使用原生WebSocket处理SignalR Hub消息时无法正常工作:onopen函数触发,但onmessage函数未触发,网络面板也看不到该WebSocket的消息。

问题代码(原生WebSocket实现)

// 使用原生WebSocket测试,无法接收消息
let url = "https://localhost:7143/chatHub";

const headers = {};
const [name, value] = getUserAgentHeader();
headers[name] = value;

fetch(`${url}/negotiate?negotiateVersion=1`, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        headers, // 错误:headers嵌套
    },
    body: JSON.stringify({
        content: "", // 多余字段
        headers: headers, // 多余字段
        timeout: 100000, // 多余字段
        withCredentials: true,
    }),
}).then((response) => response.json())
    .then((response) => {
        console.log(response);

        // 构建WebSocket URL
        const wsUrl = `${url}?id=${response.connectionToken}`.replace(/^http/, "ws"); // 错误:缺少必要参数
        const webSocket = new WebSocket(wsUrl);

        webSocket.onopen = (event) => {
            console.log(event);
            // 错误:未发送SignalR初始化帧
        }

        webSocket.onmessage = (message) => {
            console.log(message);
        }

        webSocket.onerror = (event) => {
            console.log(event);
            console.log(event.error);
        }

        webSocket.onclose = (event) => {
            console.log(event);
        }
});

// 从SignalR库中提取的辅助函数
function getUserAgentHeader() {
    let userAgentHeaderName = "X-SignalR-User-Agent";

    return [userAgentHeaderName, constructUserAgent("7.0.5", undefined, "Browser", undefined)];
}
function constructUserAgent(version, os, runtime, runtimeVersion) {
    let userAgent = "Microsoft SignalR/";
    const majorAndMinor = version.split(".");
    userAgent += `${majorAndMinor[0]}.${majorAndMinor[1]}`;
    userAgent += ` (${version}; `;
    if (os && os !== "") {
        userAgent += `${os}; `;
    }
    else {
        userAgent += "Unknown OS; ";
    }
    userAgent += `${runtime}`;
    if (runtimeVersion) {
        userAgent += `; ${runtimeVersion}`;
    }
    else {
        userAgent += "; Unknown Runtime Version";
    }
    userAgent += ")";
    return userAgent;
}

使用原生WebSocket时的网络面板:无消息接收记录

错误分析与修正方案

1. 协商请求的Headers格式错误

你在fetch的headers中嵌套了headers对象,导致服务端无法正确解析X-SignalR-User-Agent头。同时请求体中包含了多余的字段(content、headers、timeout),这些字段SignalR协商接口不需要。

修正后的协商请求:

fetch(`${url}/negotiate?negotiateVersion=1`, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        [name]: value // 直接添加用户代理头
    },
    body: JSON.stringify({
        withCredentials: true // 只保留必要字段
    })
})

2. WebSocket连接后未发送初始化帧

SignalR的WebSocket传输要求客户端在连接成功后,必须发送一个初始化消息,告知服务端使用的协议和版本。服务端只有收到这个消息后,才会将客户端加入连接池并推送消息。

修正后的onopen回调:

webSocket.onopen = (event) => {
    console.log("WebSocket连接成功");
    // 发送SignalR初始化帧,必须符合JSON协议格式
    const initMessage = JSON.stringify({
        protocol: "json",
        version: 1
    });
    webSocket.send(initMessage);
}

3. WebSocket URL缺少必要参数

构建WebSocket URL时,需要添加transport=webSockets参数,明确告知服务端使用WebSocket传输类型。

修正后的URL构建:

const wsUrl = `${url}?id=${response.connectionToken}&transport=webSockets`.replace(/^http/, "ws");

完整修正后的代码

let url = "https://localhost:7143/chatHub";

const [name, value] = getUserAgentHeader();

fetch(`${url}/negotiate?negotiateVersion=1`, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        [name]: value
    },
    body: JSON.stringify({
        withCredentials: true
    })
}).then((response) => response.json())
    .then((response) => {
        console.log(response);

        const wsUrl = `${url}?id=${response.connectionToken}&transport=webSockets`.replace(/^http/, "ws");
        const webSocket = new WebSocket(wsUrl);

        webSocket.onopen = (event) => {
            console.log("WebSocket连接建立");
            // 发送初始化帧
            webSocket.send(JSON.stringify({
                protocol: "json",
                version: 1
            }));
        }

        webSocket.onmessage = (message) => {
            console.log("收到消息:", message.data);
            // 解析SignalR消息:SignalR的消息是JSON数组,需要处理
            const data = JSON.parse(message.data);
            if (data.length > 0) {
                // 处理具体的消息,比如methodeTestBis
                data.forEach(item => {
                    if (item.target === "methodeTestBis") {
                        console.log("methodeTestBis参数:", item.arguments);
                    }
                });
            }
        }

        webSocket.onerror = (event) => {
            console.error("WebSocket错误:", event);
        }

        webSocket.onclose = (event) => {
            console.log("WebSocket关闭:", event);
        }
});

function getUserAgentHeader() {
    let userAgentHeaderName = "X-SignalR-User-Agent";
    return [userAgentHeaderName, constructUserAgent("7.0.5", undefined, "Browser", undefined)];
}

function constructUserAgent(version, os, runtime, runtimeVersion) {
    let userAgent = "Microsoft SignalR/";
    const majorAndMinor = version.split(".");
    userAgent += `${majorAndMinor[0]}.${majorAndMinor[1]}`;
    userAgent += ` (${version}; `;
    if (os && os !== "") {
        userAgent += `${os}; `;
    } else {
        userAgent += "Unknown OS; ";
    }
    userAgent += `${runtime}`;
    if (runtimeVersion) {
        userAgent += `; ${runtimeVersion}`;
    } else {
        userAgent += "; Unknown Runtime Version";
    }
    userAgent += ")";
    return userAgent;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 03:12:09