使用原生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
相关产品推荐
相关产品推荐

