WebSocket 原理与面试题解析
一、WebSocket 出现前的实时场景解决方案
1. 常见的实时应用场景
- 金融领域: 股票 K 线图、实时报价。
- 即时通讯: 网页聊天室、在线客服。
- 系统通知: 紧急警报(如地震预警)、重要消息推送。
- 实时数据: 票务系统的余票信息、抢购活动的实时库存。
这些场景的共同特点是 对数据的实时性要求非常高。
2. 问题的根源:HTTP 的请求-响应模式
传统的 Web 通信基于 HTTP 协议,其核心是 请求-响应模式。
- 工作流程: 必须由客户端(浏览器)首先发起请求,服务器才能返回响应。
- 核心限制: 服务器无法主动向客户端推送(Push)数据。
以聊天为例:
- 用户 A 通过 HTTP 请求将消息 "你好" 发送给服务器。
- 服务器响应用户 A,表示消息已收到。
- 服务器 无法 主动将消息推送给用户 B,因为它不能主动发起连接。
- 只有当用户 B 向服务器发起请求时,服务器才能在响应中把消息"你好"捎带给用户 B。但问题是,用户 B 不知道何时会有新消息,因此也不知道何时应该发起请求。
3. 旧方案一:短轮询 (Short Polling)
短轮询是一种模拟实时通信的“话唠式”方案。
原理: 客户端使用定时器(如
setInterval),每隔一个很短的时间(例如 1-2 秒)就向服务器发送一次 HTTP 请求,询问是否有新数据。流程图解:
Client: Server: | --(有新消息吗?)--> | | <--(暂时没有)---- | (完成一次HTTP请求/响应,TCP连接关闭) | | (等待1秒) | | | | --(有新消息吗?)--> | | <--(暂时没有)---- | (完成一次HTTP请求/响应,TCP连接关闭) | | (等待1秒) | | | | --(有新消息吗?)--> | | <--(有!给你数据) | (完成一次HTTP请求/响应,TCP连接关闭) | ... |优点:
- 实现简单,兼容性好。
缺点:
- 产生大量无意义请求: 大多数请求可能都是空轮询,服务器并没有新数据,浪费带宽和服务器资源。
- 频繁建立和关闭连接: 每次请求都伴随着 TCP 的三次握手和四次挥手,开销巨大。
- 实时性不佳: 消息的延迟取决于轮询间隔。间隔太短则服务器压力大,间隔太长则消息延迟高。
4. 旧方案二:长轮询 (Long Polling)
长轮询是短轮询的改进版,旨在减少无效请求。
原理: 客户端发送一个 HTTP 请求到服务器。服务器收到后,如果有新消息则立即响应;如果没有新消息,则 挂起(Hold) 这个连接,直到有新消息或连接超时为止。客户端在收到响应或请求超时后,立即发起下一次长轮询请求。
流程图解:
Client: Server: | --(有新消息吗?)--> | | | (服务器收到请求,但没有新消息,保持连接) | | | ...等待... | (一段时间后,服务器有了新消息) | | | <--(有!给你数据) | (服务器响应,本次连接关闭) | | | --(有新消息吗?)--> | (客户端立即发起下一次请求) | | | ...等待... |优点:
- 显著减少了无效的请求次数,节省了带宽。
- 实时性相对较高,一旦有数据就能立即响应。
缺点:
- 服务器资源占用: 服务器需要长时间挂起连接,如果客户端数量庞大,会对服务器造成较大的内存和连接数压力。
- 连接超时问题: HTTP 请求有超时限制,如果长时间没有新消息,连接会因超时而中断。客户端需要处理超时逻辑,立即重新发起请求,这会产生一些无意义的连接。
二、WebSocket 详解
WebSocket 从根本上解决了问题,它是一种全新的、独立的协议。
1. 核心原理:全双工通信
- HTTP: 半双工(Half-duplex)。数据传输有固定的先后顺序(请求->响应)。
- WebSocket: 全双工(Full-duplex)。连接一旦建立,通信双方地位平等,可以在任意时刻互相发送数据,互不干扰。
WebSocket 建立在 TCP 协议之上,充分利用了 TCP 的全双工通信能力,解决了 HTTP 协议中服务器无法主动推送消息的限制。
2. WebSocket 连接过程
WebSocket 的生命周期分为两个阶段:握手阶段 和 通信阶段。
- 建立 TCP 连接: 和 HTTP 一样,首先需要通过 TCP 的三次握手建立底层连接。
- WebSocket 握手: TCP 连接建立后,客户端会通过一个特殊的 HTTP 请求与服务器进行“握手”,协商从 HTTP 协议升级到 WebSocket 协议。
- 数据传输: 握手成功后,连接升级为 WebSocket 连接,双方可以开始自由地进行全双工通信。
- 关闭 TCP 连接: 当一方请求关闭或连接中断时,通过 TCP 的四次挥手关闭连接。
3. WebSocket 的优缺点
优点:
- 真正的实时性: 服务器可以主动推送数据,延迟极低。
- 减少开销: 只需一次握手即可建立持久连接,后续数据传输无需再发送冗余的 HTTP 头。
- 更好的性能: 协议开销小,数据帧格式轻量,传输效率高。
缺点:
- 兼容性: 作为 HTML5 新增协议,一些非常古老的浏览器可能不支持。
- 服务器资源: 维持一个长连接需要占用服务器一定的内存和连接资源。对于消息量稀少的场景,可能会造成资源浪费。
4. WebSocket 能否取代 Ajax?
不能。 它们适用于不同的场景。
- Ajax/HTTP: 适用于“请求-响应”模式明确的场景,如获取文章列表、提交表单等。这些操作通常是短暂、一次性的,用完即弃,无需维持长连接。
- WebSocket: 适用于需要高实时性、频繁双向通信的场景,如聊天、游戏、协同编辑等。
优化思考: 在一个必须使用 WebSocket 的页面(如聊天页面),为了充分利用已建立的持久连接,理论上可以将页面内其他的 Ajax 请求也通过 WebSocket 来完成,从而避免发起新的 HTTP 请求。但这样做会大大增加程序设计的复杂性(前后端都需要适配两种通信逻辑),因此在实践中很少使用。
三、WebSocket 握手过程 (面试重点)
握手阶段的本质是 “借用 HTTP 协议来完成协议升级的协商”。
1. 客户端请求 (Handshake Request)
客户端发起一个特殊的 HTTP/1.1 GET 请求。
请求地址: 协议名由
http://变为ws://(或https对应wss://)。ws://your.server.com/pathwss://your.server.com/path(WSS 运行在 TLS/SSL 之上,是加密的)
关键请求头:
httpGET /chat HTTP/1.1 Host: server.example.com Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Connection: Upgrade: 告诉服务器,客户端希望升级协议。Upgrade: websocket: 明确指出希望升级到 websocket 协议。Sec-WebSocket-Version: 13: 指定 WebSocket 协议版本,目前常用的是 13。Sec-WebSocket-Key: 一个 Base64 编码的随机字符串,用作简单的“暗号”,防止一些意外的或恶意的连接。
2. 服务器响应 (Handshake Response)
如果服务器同意升级协议,会返回一个特殊的响应。
状态码:
101 Switching Protocols,表示服务器同意切换协议。关键响应头:
httpHTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Connection: Upgrade&Upgrade: websocket: 同样返回这两个头,确认协议升级。Sec-WebSocket-Accept: 这是服务器对客户端Sec-WebSocket-Key的“回令”。它的值是服务器将客户端的Sec-WebSocket-Key与一个固定的“魔术字符串” (258EAFA5-E914-47DA-95CA-C5AB0DC85B11) 拼接后,计算 SHA-1 哈希,再进行 Base64 编码得到的。客户端会验证这个值,确认服务器是真正的 WebSocket 服务器。
握手成功后,该 HTTP 连接的生命周期结束,TCP 通道被 WebSocket 协议接管,后续通信都遵循 WebSocket 的帧格式,不再有 HTTP 的请求和响应。
四、相关面试题汇总
1. 什么是 WebSocket 协议?请简述一下。
WebSocket 是 HTML5 提供的一种在单个 TCP 连接上进行全双工通信的协议。它是一个持久化的连接协议,与 HTTP 不同,它允许服务器主动向客户端推送数据。其连接过程首先通过 HTTP 协议进行一次握手,握手成功后协议升级为 WebSocket,之后的数据交换都通过这个持久的 TCP 通道进行,实现了真正的实时通信。
2. WebSocket 与传统 HTTP 相比有什么优势?
- 通信模式: WebSocket 是全双工通信,服务器可以主动推送消息,解决了 HTTP 服务器只能被动响应的限制。相比之下,HTTP 必须由客户端发起请求。
- 性能开销: WebSocket 建立连接后,后续数据传输的头部开销非常小,而 HTTP 每个请求都带有完整的、通常是冗余的头部信息。
- 实时性: WebSocket 提供了真正的实时数据传输能力,而传统的解决方案如短轮询和长轮询都有延迟高、消耗资源等问题。长轮询虽然实时性较好,但会长时间占用服务器连接资源。
3. 前端如何实现即时通信?
主要有三种方案:
- 短轮询 (Short Polling): 客户端定时向服务器发送请求。实现简单,但实时性差、资源浪费严重。
- 长轮询 (Long Polling): 客户端发送请求后,服务器会挂起连接直到有数据或超时。相比短轮询有较大改进,但仍存在服务器资源占用和连接超时的问题。
- WebSocket: 目前实现即时通信的最佳方案。它通过一次握手建立持久的全双工连接,允许服务器主动、实时地向客户端推送数据,性能好,开销小。
4. 简述 WebSocket 的握手过程。
WebSocket 的握手过程借用了 HTTP 协议。
- 客户端请求: 客户端向服务器发起一个 HTTP GET 请求,请求头中包含
Connection: Upgrade和Upgrade: websocket,表示请求升级协议。同时会带上一个随机生成的Sec-WebSocket-Key作为标识。- 服务器响应: 服务器如果同意升级,会返回状态码为
101 Switching Protocols的响应,响应头中也包含Connection: Upgrade和Upgrade: websocket。同时,服务器会使用客户端的Sec-WebSocket-Key按照约定算法计算出一个Sec-WebSocket-Accept值并返回给客户端进行验证。- 握手完成: 客户端验证通过后,握手成功,HTTP 连接升级为 WebSocket 连接,双方可以开始自由通信。