什么是WebSocket?基础知识梳理

聊天应用和实时行情为什么能在不刷新页面的情况下即时更新?答案就在WebSocket。下面从它要解决的问题讲起,说明它的工作方式、适用场景和上线时需要注意的事。

什么是WebSocket

普通的HTTP通信是“请求-响应”模式:服务器只有在客户端发出请求之后才能回应,因此无法主动把新信息告诉客户端。WebSocket(标准为RFC 6455)突破了这一限制,让一条连接保持打开,服务器和客户端任何一方都可以随时发送消息,实现真正的双向实时通信。它在浏览器里通过WebSocket对象使用,在服务器端则有各种语言的库可用。

WebSocket出现之前的方式:轮询

在WebSocket出现之前,网站通过轮询来模拟实时效果:客户端每隔几秒就反复问服务器“有新消息吗?”。短轮询会用大量请求给服务器和网络带来不必要的压力,而且多数请求得到的答案是“没有”。长轮询让服务器延迟响应,直到真有新事件才回复,有所改善,但每次请求仍带着完整的HTTP头部,依然不是真正的双向通信。WebSocket用一条持久连接取代了这种反复请求。

握手与协议升级

WebSocket连接起初是一个普通的HTTP请求。客户端发送带有Upgrade: websocket头部的请求,并附上一个随机的Sec-WebSocket-Key;服务器接受后,用HTTP状态码101(Switching Protocols)响应,并在Sec-WebSocket-Accept中返回根据该密钥计算出的值,用来证明它确实理解WebSocket。这一步之后,同一条TCP连接不再按HTTP的请求-响应方式运行,而是变成双方随时都能发送数据帧的WebSocket连接。

“ws://”与“wss://”的区别

ws://是未加密的WebSocket连接,wss://则是经过TLS加密的WebSocket连接,关系和HTTP与HTTPS一样。传输任何敏感数据都应使用wss://,以防止被窃听或篡改。出于安全考虑,浏览器通常也会阻止从HTTPS页面发起不加密的ws://连接,所以上线的网站基本都要使用wss://。

数据帧、消息与控制帧

连接建立后,数据以“帧”的形式传输,一条消息可以拆成多个帧。帧分为数据帧(文本或二进制)和控制帧(关闭、Ping、Pong)。按协议规定,浏览器等客户端发往服务器的帧必须经过掩码处理,服务器发给客户端的帧则不能掩码;这样做是为了避免某些旧代理被恶意构造的内容误导。控制帧的内容很短,协议限定在125字节以内。

心跳与断线重连

WebSocket连接可能在双方都没察觉的情况下悄然断开,比如手机从Wi-Fi切到蜂窝网络,或电脑进入休眠。实际应用通常会定期发送Ping/Pong心跳帧,或在应用层发送自定义心跳,以便尽快发现失效的连接。一旦发现断线,客户端要有自动重连逻辑,并采用逐渐拉长的重试间隔,避免服务器恢复时被大量同时重连的客户端压垮。

实际应用场景

聊天应用中消息无需刷新即可显示,股票和加密货币行情实时跳动,在线游戏中玩家状态同步,多人协作文档里彼此的光标和编辑内容可见,以及监控面板、实时通知、拍卖或体育比分等场景,都是WebSocket或类似技术的典型用途。这些场景的共同点是:数据更新频繁且无法预知,延迟需要很低。

WebSocket与替代技术的比较

如果只需要服务器向客户端推送数据,Server-Sent Events(SSE)可能是更简单的选择:它基于普通HTTP,浏览器内置自动重连,更容易融入现有基础设施。对于聊天、游戏等客户端也要频繁发消息的场景,WebSocket的双向通信更合适。如果更新并不频繁,每隔一段时间的普通轮询甚至更省事。选择技术应当取决于实际需求,而不是追求时髦。

WebSocket与HTTP之间的关系

人们容易把WebSocket想象成取代HTTP的东西,但实际上它是以一个HTTP请求开始的,握手成功(状态码101)之后才切换协议。这意味着WebSocket服务器仍要正确处理最初的HTTP请求,而一些只理解普通HTTP的基础设施,比如老旧的代理服务器或限制严格的防火墙,有时会干扰这次升级。在反向代理(如Nginx)后面运行WebSocket服务时,需要显式配置对Upgrade头的转发,否则连接会在握手阶段失败。

为什么生产环境中的WebSocket应用需要额外的配套机制

基础的WebSocket连接会悄然断开,所以实际应用要加入心跳和自动重连。此外,和无状态的HTTP请求不同,每一条WebSocket连接都是长期存在且带状态的,这意味着把服务横向扩展到多台服务器时,需要额外的处理:常见做法是使用会话保持(sticky session),或者用共享的消息代理(如Redis等)在各台服务器之间转发消息,确保消息能送到持有对应连接的那台服务器。还需要限制单个连接的消息大小和频率,并对来源和身份进行校验。

安全上需要注意什么

WebSocket握手不受浏览器同源策略的约束,服务器必须自行检查Origin头,以防跨站WebSocket劫持(CSWSH):恶意网页借用户已登录的身份发起连接。身份验证通常在握手阶段或连接建立后的第一条消息完成,不要把长期有效的令牌直接放在URL里,因为它们可能被记入日志。所有来自客户端的消息都应当当作不可信输入处理。

常见问题

防火墙会阻止WebSocket连接吗?

可能会。一些管控严格的企业网络或公共Wi-Fi防火墙,会阻止80和443之外的端口,或者阻止协议升级请求。不过大多数WebSocket服务直接使用HTTPS通常使用的443端口,从而把这种风险降到最低。

WebSocket在移动网络环境下也能稳定运行吗?

在Wi-Fi与蜂窝网络之间切换、屏幕熄灭或应用转入后台时,连接都可能断开,所以实际应用通常内置自动重连和心跳逻辑,专门应对这类中断。

WebSocket一定比HTTP快吗?

不一定。它省去了每条消息重复的握手和头部开销,所以在频繁的小消息场景里延迟更低、更省资源。但对于偶尔一次的普通请求,HTTP更简单,也更容易缓存和扩展。

一个服务器能同时维持多少条WebSocket连接?

没有固定的数字,取决于服务器的内存、网络、使用的语言与框架以及消息频率。通常需要通过压力测试来确认,并在设计时考虑横向扩展。

前端用WebSocket时,最基本的代码长什么样?

浏览器端大致是三步:用new WebSocket加上wss地址创建连接,通过onmessage监听收到的消息,再用send发送消息。此外还应处理onclose和onerror事件,以便在断线时触发重连。

WebSocket能传输二进制数据吗?

可以。除了文本帧,协议还支持二进制帧,因此可以传输图片片段、音频或自定义的紧凑数据格式,这在游戏和音视频场景中很常见。