一、为什么我们需要自动切换传输方式

想象一下你正在开发一个在线聊天应用,用户A用最新版Chrome浏览器,用户B却还在用老旧的IE11。这时候如果只依赖WebSocket,用户B可能完全无法使用——这就是SignalR的聪明之处:它能自动在WebSocket、长轮询等传输方式间切换,确保所有用户都能正常连接。

SignalR默认会优先尝试WebSocket(最高效的实时通信方式),但当遇到以下情况时会自动降级:

  1. 浏览器不支持WebSocket(如IE9及以下)
  2. 企业防火墙阻止WebSocket协议
  3. 移动网络不稳定导致WebSocket频繁断开
// 技术栈:JavaScript + SignalR  
// 基础连接示例
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chatHub")
    .configureLogging(signalR.LogLevel.Information)  // 开启日志便于调试
    .build();

// 尝试启动连接(自动选择最佳传输方式)
connection.start()
    .then(() => console.log("连接成功!当前传输方式:", connection.connection.connectionFeatures.transport.name))
    .catch(err => console.error("连接失败:", err));

二、手动配置传输策略的实战技巧

虽然自动切换很省心,但有时我们需要精细控制。比如在监控系统中,你可能希望强制使用WebSocket以保证实时性,而在文件上传场景则更适合长轮询。

// 技术栈:JavaScript + SignalR  
// 手动配置传输优先级示例
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/dataHub", {
        skipNegotiation: false,  // 是否跳过协商步骤(默认false)
        transport: signalR.HttpTransportType.WebSockets 
            | signalR.HttpTransportType.LongPolling  // 允许两种传输方式
    })
    .build();

// 监听传输方式变化
connection.onreconnecting((error) => {
    console.warn("正在重连...", error);
    // 此时SignalR会自动尝试备选传输方式
});

关键参数说明

  • skipNegotiation: 设为true时直接使用WebSocket(需服务端支持)
  • transport: 按位组合指定允许的传输方式(WebSockets/LongPolling/ServerSentEvents)

三、深度解决浏览器兼容性问题

不同浏览器的兼容性陷阱及解决方案:

1. IE浏览器的特殊处理

IE10/11需要额外引入ES6 Promise的polyfill:

<!-- 在SignalR脚本前引入 -->
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.8/dist/es6-promise.auto.min.js"></script>

2. Safari的协议限制

Safari会拒绝非加密的WebSocket连接(即ws://),必须使用wss://。解决方案:

// 根据当前协议自动切换
const hubUrl = window.location.protocol === 'https:' 
    ? 'wss://yourdomain.com/dataHub' 
    : 'ws://yourdomain.com/dataHub';

3. 微信内置浏览器的长轮询优化

微信浏览器可能频繁断开长轮询连接,需要调整超时设置:

.withUrl("/wechatHub", {
    transport: signalR.HttpTransportType.LongPolling,
    headers: { "X-Requested-With": "XMLHttpRequest" }  // 避免被微信拦截
})

四、性能优化与异常处理实战

1. 心跳检测配置

防止长时间无通信被防火墙断开:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/heartbeatHub", {
        withCredentials: true,
        keepAliveIntervalInMilliseconds: 15000  // 每15秒发送心跳包
    })
    .build();

2. 重连策略进阶版

// 自定义重试逻辑
async function startConnection() {
    let retries = 0;
    const maxRetries = 5;
    
    while (retries < maxRetries) {
        try {
            await connection.start();
            break;
        } catch (err) {
            retries++;
            const delay = Math.min(1000 * retries, 5000);
            console.log(`第${retries}次重试,${delay}ms后重连...`);
            await new Promise(resolve => setTimeout(resolve, delay));
        }
    }
}

五、经典场景与最佳实践

场景1:实时股票行情

  • 需求特点:高频更新、低延迟
  • 配置方案
transport: signalR.HttpTransportType.WebSockets,
skipNegotiation: true  // 直接建立WebSocket连接

场景2:工单状态通知

  • 需求特点:兼容老旧系统、高可靠性
  • 配置方案
transport: signalR.HttpTransportType.LongPolling,
serverTimeoutInMilliseconds: 30000  // 延长超时时间

场景3:多人在线协作编辑

  • 需求特点:需要冲突解决、断线恢复
  • 特殊处理
// 记录最后接收的消息ID
let lastMessageId = 0;
connection.on("ReceiveUpdate", (data) => {
    if (data.id > lastMessageId) {
        applyUpdate(data.content);
        lastMessageId = data.id;
    }
});

六、总结与决策指南

技术选型建议

  • 现代浏览器优先用WebSocket
  • 需要穿透防火墙时启用LongPolling备用
  • 移动端考虑增加心跳间隔

常见坑点

  1. 不要在生产环境关闭configureLogging
  2. iOS设备休眠会导致WebSocket断开,需要监听页面可见性变化
  3. 当同时配置多个传输方式时,注意测试每种组合的兼容性

通过合理配置,SignalR能帮你轻松应对99%的实时通信场景。记住:没有完美的传输方式,只有最适合当前环境的解决方案!