Chrome/Firefox/Safari的WebSocket并发连接数卡在6个,是因为浏览器对同源TCP连接统一限制为6个(含XHR、fetch、图片及WebSocket),属同源策略下的客户端管控机制,非协议或bug;WebSocket不复用HTTP/2多路复用,始终独占TCP连接。

Chrome/Firefox/Safari 的 WebSocket 并发连接数为什么卡在 6 个?
不是 bug,是浏览器对同源 TCP 连接的统一管控策略。WebSocket 复用 HTTP/1.1 的连接池,每个域名(含子域名)默认最多建立 6 个并发 TCP 连接——这包括 XMLHttpRequest、fetch、图片、脚本加载,以及所有 WebSocket 实例。你调 new WebSocket('wss://api.example.com') 十次,第 7 次起就会阻塞或直接触发 onerror,错误信息通常是 net::ERR_CONNECTION_REFUSED 或静默超时。
关键点:
-
Sec-WebSocket-Version: 13是现代标准,但 Safari 5.x 等极旧版本仍用7.6,握手失败时不会报错,而是直接关闭连接 - Firefox 可通过
network.http.max-persistent-connections-per-server修改,但仅限本地配置,前端 JS 无法读写该值 - HTTP/2 不提升 WebSocket 并发数:WebSocket 总是走独立 TCP 连接,不享受 HTTP/2 多路复用
IE9–10 和 Android 4.3 以下怎么连上 WebSocket?
原生 WebSocket 构造函数在 IE9 报 Object doesn't support property or method 'WebSocket';Android 4.3 WebView 中 WebSocket 存在但握手失败率高,且不支持 binaryType = 'arraybuffer'。
不要自己写降级轮询逻辑。直接用 SockJS:
- 它会自动尝试
WebSocket→XHR-streaming→iframe-eventsource→jsonp-polling,兼容到 IE6 - 服务端必须配对应后端:如 Node.js 用
sockjs-node,Go 用gorilla/sockjs,不能只跑原生gorilla/websocket - 注意路径前缀:客户端
new SockJS('https://api.example.com/realtime')要和服务端注册的 endpoint 完全一致,否则 404
如何验证当前页面是否已触达浏览器连接上限?
不能靠计数器,因为连接可能处于 connecting 状态但未完成握手,或已被内核悄悄复用/回收。可靠方式是监听连接行为本身:
- 监听所有
WebSocket实例的onerror:若频繁出现CLOSE_ABNORMAL或无任何回调就断开,大概率是连接被限 - 用 Chrome DevTools → Network → Filter 输入
ws,看连接状态列是否大量显示Pending或Failed - 抓包确认:Wireshark 过滤
tcp.port == 443 and tls.handshake.type == 1,若 TLS 握手请求密集发出但无响应,就是客户端连接池耗尽
别依赖 navigator.onLine——它只反映网络通断,和 WebSocket 连接池无关。
子域名分片和单连接多路复用,哪个更稳?
优先选 单连接多路复用。子域名方案看似简单,实则引入 DNS 解析延迟、证书管理成本、CDN 缓存分裂等问题。而复用一个连接,只要协议设计得当,能规避 90% 的并发瓶颈。
实操要点:
- 定义轻量路由字段,如
{ "type": "user:status", "id": "u123", "data": {} },避免嵌套过深或字段爆炸 - 前端用 Map 存储
type→callback映射,收到消息后callbacks.get(msg.type)?.(msg.data) - 服务端不做协议解析——只透传,把路由逻辑完全交给前端,降低服务端耦合与序列化开销
- 为防单点故障,可搭配
EventSource做低频兜底:比如用户离线状态同步改用 SSE,不挤占 WebSocket 通道
真正容易被忽略的是心跳保活粒度:不要全局 ping/pong,而是按业务通道分级——聊天通道每 30s 发心跳,监控数据通道可设为 5s,否则弱网下大量无意义帧反而加剧丢包。


















