本文档完全贴合你9年前端架构岗面试定位,结合你落地过的扫码登录、币安实时交易、DEX行情推送等项目场景,精简核心用法、重点强化面试高频考点,同时补充你项目中用到的SSE与WebSocket的核心对比,可直接背诵用于面试。
WebSocket是HTML5提供的全双工、持久化TCP通信协议,基于HTTP完成握手后,客户端与服务端可双向实时推送数据,无需客户端重复发起请求,彻底解决了HTTP轮询的延迟高、资源浪费问题,是实时交易、扫码登录、行情推送、实时通知等场景的工业级标准方案。
| API | 核心作用 | 面试重点 |
|---|---|---|
new WebSocket(url) | 创建WebSocket连接 | 生产环境必须用wss://(加密协议,基于HTTPS 443端口),开发环境用ws:// |
ws.onopen | 连接建立成功的回调 | 连接成功后启动心跳保活、刷新断线消息队列 |
ws.onmessage | 接收服务端推送消息的回调 | 统一处理消息分发、二进制数据解析 |
ws.send(data) | 向服务端发送消息 | 支持字符串、ArrayBuffer、Blob,发送前必须校验连接状态 |
ws.onclose | 连接关闭的回调 | 非正常关闭触发断线重连逻辑 |
ws.onerror | 连接错误的回调 | 捕获异常,触发重连 |
ws.readyState | 当前连接状态 | 4种状态:0=连接中、1=已连接、2=关闭中、3=已关闭,发送消息前必须判断状态为1 |
class WebSocketClient {
private ws: WebSocket | null = null;
private readonly url: string;
private reconnectCount = 0;
private maxReconnect = 5; // 最大重连次数
private reconnectDelay = 2000; // 重连延迟
private heartbeatTimer: NodeJS.Timeout | null = null;
private heartbeatInterval = 30000; // 30秒心跳
private messageQueue: any[] = []; // 断线消息队列
constructor(url: string) {
this.url = url;
}
// 初始化连接
connect() {
try {
this.ws = new WebSocket(this.url);
this.bindEvents();
} catch (err) {
console.error('连接创建失败', err);
this.reconnect();
}
}
// 绑定核心事件
private bindEvents() {
if (!this.ws) return;
// 连接建立成功
this.ws.onopen = () => {
console.log('WebSocket连接成功');
this.reconnectCount = 0;
this.startHeartbeat(); // 启动心跳
this.flushQueue(); // 发送断线期间的消息
};
// 接收服务端消息
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 心跳响应
if (data.type === 'pong') return;
// 业务消息分发(如扫码状态、交易行情)
this.handleMessage(data);
} catch (err) {
console.error('消息解析失败', err);
}
};
// 连接关闭
this.ws.onclose = (event) => {
console.log('连接关闭', event.code, event.reason);
this.stopHeartbeat();
// 非正常关闭触发重连
if (!event.wasClean) this.reconnect();
};
// 连接错误
this.ws.onerror = (err) => {
console.error('连接错误', err);
};
}
// 发送消息(断线自动入队)
send(data: any) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
} else {
this.messageQueue.push(data);
}
}
// 业务消息处理(适配你的项目场景)
private handleMessage(data: any) {
switch (data.type) {
case 'qrcode_scanned':
// 扫码登录-已扫码回调
updateQRCodeStatus('scanned');
break;
case 'qrcode_confirmed':
// 扫码登录-已确认回调
loginWithTempToken(data.tempToken);
break;
case 'trade_ticker':
// 交易行情推送
updateTicker(data.data);
break;
default:
break;
}
}
// 心跳保活(防止连接假死,生产必备)
private startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
}
}, this.heartbeatInterval);
}
private stopHeartbeat() {
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
}
// 断线重连(生产必备)
private reconnect() {
if (this.reconnectCount >= this.maxReconnect) {
console.error('重连次数超限');
alert('连接已断开,请刷新页面重试');
return;
}
this.reconnectCount++;
setTimeout(() => this.connect(), this.reconnectDelay * this.reconnectCount); // 延迟递增
}
// 刷新消息队列(连接成功后发送断线消息)
private flushQueue() {
while (this.messageQueue.length) {
const msg = this.messageQueue.shift();
this.send(msg);
}
}
// 关闭连接
close() {
this.stopHeartbeat();
this.ws?.close();
}
}
// 项目中使用示例
// 1. 扫码登录场景
const qrcodeWs = new WebSocketClient('wss://api.your-domain.com/qrcode/ws?uuid=xxx');
qrcodeWs.connect();
// 2. 交易行情场景
const tickerWs = new WebSocketClient('wss://api.your-domain.com/trade/ticker');
tickerWs.connect();
你项目中写了SSE,面试官一定会问「为什么用SSE不用WebSocket?」「两者有什么区别?」,下表直接背:
| 对比维度 | WebSocket | SSE(Server-Sent Events) |
|---|---|---|
| 通信模式 | 全双工,客户端<->服务端双向通信 | 单工,仅服务端向客户端单向推送 |
| 协议基础 | 独立的TCP协议,基于HTTP握手升级 | 纯HTTP协议,无需升级 |
| 数据格式 | 支持字符串、二进制数据(图片/音频/流数据) | 仅支持UTF-8字符串 |
| 适用场景 | 双向通信场景:实时聊天、扫码登录、在线协作、交易下单、游戏 | 纯服务端推送场景:新闻推送、行情播报、日志实时输出、公告通知 |
| 断线重连 | 需要手动实现心跳+重连逻辑 | 浏览器原生支持自动断线重连,自带last-event-id实现断点续传 |
| 兼容性 | 所有现代浏览器支持,IE10+ | 所有现代浏览器支持,IE/Edge Legacy不支持(可通过polyfill兼容) |
| 资源占用 | 长连接常驻,双向通信无额外开销 | 基于HTTP长连接,单向推送开销更低 |
| 防火墙穿透 | 部分老旧企业防火墙会拦截WebSocket协议 | 纯HTTP协议,无拦截问题 |
【面试专属回答】为什么你项目里用了SSE,而不是WebSocket?
我在项目中会根据业务场景做技术选型:
- 对于纯服务端单向推送的场景,比如系统公告、行情数据播报、日志实时输出,我会优先用SSE。因为它基于纯HTTP协议,无需处理双向通信的复杂度,浏览器原生支持自动重连和断点续传,开发和维护成本更低,开销也更小;
- 对于需要双向通信的场景,比如扫码登录、交易下单、在线协作,我会用WebSocket。因为它支持全双工通信,客户端可以实时向服务端发送消息,延迟更低,更适合双向交互的场景;
- 两者不是互斥的,我在币安的交易系统里,就是两者结合使用:行情推送用SSE,扫码登录、交易交互用WebSocket,最大化发挥两者的优势。
精简版:WebSocket基于HTTP完成握手,握手成功后升级为TCP长连接,和HTTP再无关系;HTTP是单向通信,WebSocket是全双工双向通信。
完整版:
- 关联:WebSocket的连接建立依赖HTTP协议,客户端先发送一个带
Upgrade: websocket头的HTTP请求,服务端同意后完成握手,连接升级为WebSocket协议,后续通信不再使用HTTP。- 核心区别:
- 通信模式:HTTP是单向通信,只能客户端发起请求,服务端被动响应;WebSocket是全双工通信,连接建立后双方可随时互相发送消息;
- 连接状态:HTTP是无状态的短连接,每次请求都要重新建立TCP连接;WebSocket是持久化长连接,一次握手后连接一直保持,直到主动关闭;
- 开销:HTTP每次请求都携带完整的请求头,冗余开销大;WebSocket连接建立后,数据传输仅需极小的帧头,开销极低;
- 实时性:HTTP轮询有2-3秒延迟,WebSocket是毫秒级实时推送。
- 客户端向服务端发送一个HTTP GET请求,携带核心请求头:
Upgrade: websocket:表示要升级为WebSocket协议;Connection: Upgrade:表示要升级连接;Sec-WebSocket-Key:随机生成的Base64字符串,用于服务端校验;Sec-WebSocket-Version: 13:WebSocket协议版本。- 服务端收到请求后,校验通过后返回HTTP 101 Switching Protocols响应,携带
Sec-WebSocket-Accept头(由客户端的Key加密生成),表示同意升级协议。- 握手完成,TCP连接保持,客户端和服务端开始双向传输WebSocket数据帧,后续通信不再使用HTTP。
有4种核心状态,是面试必背的基础考点:
0 (CONNECTING):连接正在建立中,尚未完成握手;1 (OPEN):连接已建立并打开,双方可以正常通信;2 (CLOSING):连接正在关闭中,正在进行关闭握手;3 (CLOSED):连接已关闭或连接建立失败,无法再通信。
【面试加分】发送消息前必须判断readyState === OPEN,否则会抛出JS异常,这是生产环境的基础容错处理。
这是生产环境必做的处理,也是面试高频考点,核心方案是心跳保活机制:
- 客户端心跳:连接建立成功后,客户端每30秒向服务端发送一个
ping心跳包;- 服务端响应:服务端收到
ping后,立即回复一个pong包;- 超时判断:如果客户端60秒内没有收到服务端的
pong响应,判定连接已假死,主动关闭连接并触发断线重连;- 服务端兜底:服务端如果90秒内没有收到客户端的任何消息,主动关闭连接,释放资源,避免无效连接占用服务器资源。
【场景贴合】我在币安的交易系统和扫码登录功能中,都落地了这套心跳机制,彻底解决了路由器、防火墙长时间无数据传输自动断开连接的问题。
我在生产环境落地了完整的断线重连+消息兜底方案,核心分为4步:
- 断线监听:监听
onclose事件,通过event.wasClean判断是否为正常关闭,非正常关闭(网络波动、服务端重启)自动触发重连;- 指数退避重连:设置最大重连次数(比如5次),重连延迟按2秒、4秒、8秒递增,避免频繁重连给服务端造成压力;
- 消息队列兜底:断线期间,用户发送的消息先存入本地内存队列,连接重新建立成功后,立即批量发送队列中的消息,防止消息丢失;
- 降级兜底:重连次数超限后,自动降级为HTTP轮询方案,保证核心业务(如扫码登录、行情推送)不中断,同时提示用户网络异常。
这是交易系统场景的高频考点,核心分为3点:
- 协议层面保证顺序:WebSocket基于TCP协议,TCP本身就保证了数据包的顺序传输和可靠交付,不会出现乱序问题;
- 应用层消息确认:对于交易下单、扫码确认等关键业务,我会设计应用层ACK机制:客户端发送关键消息后,服务端必须返回ACK确认包,客户端超时未收到ACK则自动重发,同时给消息加唯一ID,避免服务端重复处理;
- 断线重传兜底:连接断开后,未收到ACK的消息会存入本地队列,重连成功后自动重发,保证关键消息不丢失。
这是资深架构岗的考点,我在币安的千万级用户交易系统中,落地过对应的优化方案:
- 服务端集群化+负载均衡:用Nginx做四层负载均衡,将WebSocket连接均匀分发到多个服务节点,避免单节点压力过大;
- 连接池化管理:服务端对连接做池化管理,定期清理无效连接、假死连接,释放服务器资源;
- 消息广播优化:全平台公告等广播消息,用Redis发布订阅模式实现集群广播,避免单节点遍历所有连接的性能损耗;
- 协议优化:高频行情推送用二进制数据传输,减少数据体积,降低传输和解析开销;
- 内核参数优化:调整服务器TCP内核参数,提升最大连接数上限,优化长连接的保持时间。
这是字节、阿里等大厂必问的考点,核心风险和防护方案我在金融交易系统中都落地过,分为6点:
- 未授权访问风险:恶意用户非法连接WebSocket服务,推送/接收敏感数据。
防护:连接建立后,客户端必须在5秒内发送Token完成身份认证,服务端校验Token无效直接断开连接;同时校验请求的Cookie/Session,保证和登录态一致。- 跨域WebSocket劫持(CSWSH):恶意网站诱导用户访问,在用户不知情的情况下建立WebSocket连接,窃取敏感数据。
防护:服务端严格校验请求的Origin头,只允许白名单内的可信域名建立连接,拒绝非法Origin的请求。- 明文传输风险:
ws://是明文传输,中间人可以窃听、篡改数据。
防护:生产环境必须使用wss://加密协议,基于TLS/SSL加密,和HTTPS一致,防止中间人攻击。- 消息注入/越权操作:恶意用户发送恶意消息,越权操作其他用户的数据。
防护:服务端对每条消息都做权限校验,校验消息发送者的身份和操作权限,禁止越权操作;同时对消息内容做严格的格式校验和XSS过滤,防止注入攻击。- DOS/DDOS攻击:恶意用户建立大量无效连接,耗尽服务器资源。
防护:限制单个IP的最大连接数;限制单连接的消息发送频率,高频发送直接断开连接;设置连接认证超时,5秒内未完成认证直接断开。- 数据泄露风险:WebSocket传输敏感数据,被窃取后造成泄露。
防护:敏感数据即使在wss://下,也要做业务层加密;绝对禁止通过WebSocket传输用户密码、私钥等核心敏感信息。
我会严格根据业务场景做选型,核心判断标准如下:
- 优先用WebSocket的场景:需要客户端和服务端双向实时通信,比如扫码登录、实时聊天、在线协作、交易下单、游戏,对实时性要求高,有双向交互需求;
- 优先用SSE的场景:仅需要服务端单向推送,无客户端上行需求,比如新闻公告、行情播报、日志实时输出,开发成本低,浏览器原生支持重连,纯HTTP协议无防火墙拦截问题;
- 仅用HTTP长轮询的场景:需要兼容极端老旧浏览器,或者企业防火墙严格拦截WebSocket/SSE,作为兜底方案,缺点是延迟高、资源开销大。
这是React/Vue项目的高频考点,我在项目中的处理方案是:
- 全局复用连接:对于全应用通用的WebSocket连接(如公告通知、用户资产推送),我会把WebSocket实例放在全局状态管理(Redux/Zustand)中,路由切换时不关闭连接,全应用复用,避免重复建立连接的开销;
- 页面级连接随路由销毁:对于单页面专用的连接(如交易页行情、K线推送),我会在路由组件的
useEffect/onMounted中建立连接,在组件卸载/路由离开时,主动关闭连接,释放资源,避免无效连接占用服务器资源;- 路由切换状态保持:路由切换时,保留消息队列和重连状态,切换回来后无需重新初始化,保证用户体验的连贯性。
WebSocket是全双工的持久化TCP通信协议,基于HTTP握手建立连接,支持客户端和服务端双向实时推送,相比HTTP轮询延迟更低、资源开销更小,我在扫码登录、币安实时交易行情推送等项目中都深度落地过,同时也会根据业务场景和SSE做选型搭配。
关于WebSocket,我从核心原理、生产落地、安全防护、业务选型四个维度来讲,这套方案也是我在币安和Crypto.com的交易系统中深度落地过的生产级方案。
首先,核心原理上,WebSocket基于HTTP完成握手,升级为TCP长连接后,实现全双工双向通信,彻底解决了HTTP轮询延迟高、资源浪费的问题,核心优势就是毫秒级实时性、极低的传输开销。
然后是生产落地,我在项目中封装了完整的WebSocket客户端,核心做了四个生产级处理:一是心跳保活机制,防止连接假死;二是指数退避的断线重连,保证高可用;三是本地消息队列,防止断线期间消息丢失;四是连接状态管理,适配SPA路由切换的场景。这套方案支撑了扫码登录、交易行情实时推送等核心业务,线上连接可用性达到99.99%。
安全方面,金融交易场景对安全要求极高,我落地了完整的防护方案:生产环境强制使用wss加密协议;服务端严格校验Origin白名单,防止跨域劫持;连接建立后必须做Token身份认证,未认证直接断开;限制单IP连接数和消息频率,防止DOS攻击;所有关键消息都做权限校验,防止越权操作。
最后是选型,我不会盲目用WebSocket,会根据业务场景做选型:双向通信的场景用WebSocket,纯服务端推送的场景用SSE,极端场景用轮询兜底。比如在交易系统中,行情推送用SSE,扫码登录、交易交互用WebSocket,最大化发挥两者的优势。