前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# Socket

30 秒速记

  • Socket = 网络通信的端点,IP + 端口,是操作系统给应用的通信接口
  • TCP Socket:可靠、有序的字节流,没有消息边界 → 要自己处理粘包、拆包
  • WebSocket:HTTP 升级出来的应用层协议,按消息收发,浏览器能直接用;和 TCP Socket 不是一个层次
  • Node 里:net 模块写 TCP,dgram 写 UDP,WebSocket 用 ws 库或 Node 22 起内置的客户端
  • 长连接要管:心跳、超时、断线重连、消息去重、关闭时释放资源

Socket 可以理解成两台机器之间通信的插座,IP 加端口确定一端,操作系统通过它把数据收发出去。 前端常听到的 WebSocket 和这里的 TCP Socket 不是一回事:TCP Socket 是传输层的字节流,你发两次 hello,对方可能一次收到 hellohello,消息边界得自己定;WebSocket 是跑在 TCP 上的应用层协议,帮你把数据切成一条条消息。Node 里写 TCP 用 net 模块,几行就能起一个服务。

const net = require('net')
net.createServer((socket) => {
  socket.on('data', (buf) => socket.write('echo: ' + buf)) // 原样回显
  socket.on('error', () => socket.destroy())               // 出错要释放
}).listen(9000)
// 另开终端:nc localhost 9000,输入 hi → 收到 echo: hi
@前端进阶之旅: 代码已经复制到剪贴板

理解「Socket」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Socket 这个词在不同语境下指的东西不一样,先分清层次:

  • 操作系统层面,Socket 是进程进行网络通信的接口,由协议、本地 IP 端口、远端 IP 端口确定一条连接。
  • 传输层有 TCP 和 UDP 两种。TCP 面向连接、可靠有序,但它是字节流,没有消息边界;UDP 无连接、不保证送达,但每个数据报是独立的。
  • 应用层的 WebSocket 运行在 TCP 之上,通过 HTTP 的 Upgrade 握手建立连接,之后用帧传输消息,浏览器可以直接使用。Socket.IO 又是在 WebSocket 之上再封装的一层,自带重连、房间、降级轮询,它的客户端不能直接连原生 WebSocket 服务。

在 Node 中对应的模块:

require('net')    // TCP 服务端和客户端
require('dgram')  // UDP
require('ws')     // 第三方 WebSocket 服务端,最常用
// Node 22 起全局有 WebSocket 客户端(和浏览器同一套 API)
@前端进阶之旅: 代码已经复制到剪贴板

写长连接服务时,要额外处理这些问题:

  1. 消息边界:TCP 上要自己做分帧,常见是长度前缀或分隔符。
  2. 存活检测:网络中断时连接可能变成「半开」,要靠心跳或空闲超时发现并清理。
  3. 背压:对方读得慢时,socket.write() 会返回 false,要等 drain 事件再继续写,不然内存会一直涨。
  4. 多实例:内存里的连接表只在本进程有效,多台机器广播要借助 Redis 的发布订阅等外部消息通道。
  5. 重连与去重:断线重连后可能收到重复消息,业务上要带消息 id 做幂等。

面试官追问

追问 1客户端连续发了两条消息,服务端 data 事件只触发一次,内容粘在一起了,为什么?
参考回答

TCP 是字节流,没有消息边界,内核会合并或拆分数据。要自己定协议:按换行分隔,或者每条消息前加 4 字节长度头,服务端攒够长度再切。

追问 2TCP Socket 和 WebSocket 是什么关系?
参考回答

WebSocket 跑在 TCP 之上,先用一次 HTTP 请求升级协议,之后按帧收发消息,帮你处理了边界和掩码。浏览器出于安全不开放裸 TCP,只能用 WebSocket。

追问 3服务端测试跑完了,Node 进程却一直不退出,查什么?
参考回答

多半是还有活着的句柄:没关的连接、没 close() 的 server、没清的定时器。用 why-is-node-running 这类工具能列出来;测试里要在 afterAll 里把连接和服务都关掉。

追问 4长连接服务上线后,连接数只涨不降,怎么回事?
参考回答

客户端异常断网时,服务端未必能马上感知,死连接一直挂着。要加心跳和空闲超时,比如 socket.setTimeout(60000) 后在 timeout 事件里 destroy,或者开 setKeepAlive。

# WebSocket

30 秒速记

  • WebSocket = 一条全双工长连接,服务端能主动推,客户端也能随时发
  • 握手借 HTTP:请求带 Upgrade: websocket,服务端回 101 Switching Protocols,之后就不是 HTTP 了
  • 地址 ws:// / wss://,HTTPS 页面只能连 wss://
  • 浏览器 API:onopen / onmessage / onerror / onclose + send() / close()
  • 工程必备:心跳、断线重连(指数退避)、消息确认去重、多实例靠 Redis 发布订阅广播

WebSocket 是浏览器和服务器之间的一条长连接电话线,接通之后双方随时都能说话,不用像 HTTP 那样一问一答。 它借 HTTP 完成握手,服务端回 101 之后,这条 TCP 连接就切换成 WebSocket 协议,按帧收发消息,每帧头部只有几个字节,比每次带一堆 HTTP 头轻得多。真正难的是连接的生命周期:网断了浏览器不一定马上知道,所以要心跳;断了要自动重连,而且别所有客户端同一秒一起重连。

// Node 服务端,用 ws 库
const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({ port: 8080 })
wss.on('connection', (ws) => {
  ws.on('message', (data) => ws.send(`收到:${data}`))
})
// 浏览器:new WebSocket('ws://localhost:8080').onmessage = e => console.log(e.data)
@前端进阶之旅: 代码已经复制到剪贴板

理解「WebSocket」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

WebSocket 的连接过程分两步:先是一个普通的 HTTP/1.1 请求,带上升级头;服务端同意后,这条 TCP 连接就改为 WebSocket 协议传输帧。

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
@前端进阶之旅: 代码已经复制到剪贴板

Sec-WebSocket-Accept 是服务端用客户端的 Key 拼上一个固定字符串后做 SHA-1 再 Base64 得到的,用来证明对方真的理解 WebSocket 协议,不是什么安全机制。

浏览器端一个带心跳和重连的最小封装:

function connect(url, onMessage, retry = 0) {
  const ws = new WebSocket(url)
  let timer
  ws.onopen = () => {
    retry = 0
    timer = setInterval(() => ws.send('{"type":"ping"}'), 30000) // 心跳
  }
  ws.onmessage = (e) => onMessage(JSON.parse(e.data))
  ws.onclose = () => {
    clearInterval(timer)
    const delay = Math.min(30000, 1000 * 2 ** retry) + Math.random() * 1000
    setTimeout(() => connect(url, onMessage, retry + 1), delay) // 指数退避重连
  }
}
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
      • WebSocket
        • 一、WebSocket 解决了什么问题
        • 二、webSocket的class类
        • 三、WebSocket不稳定
        • 3.2 WebSocket心跳机制
        • 四、关于WebSocket
        • 五、WebSocket的优点
      • socket.io
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

Socket|Nodejs专题系列

# Socket

30 秒速记

  • Socket = 网络通信的端点,IP + 端口,是操作系统给应用的通信接口
  • TCP Socket:可靠、有序的字节流,没有消息边界 → 要自己处理粘包、拆包
  • WebSocket:HTTP 升级出来的应用层协议,按消息收发,浏览器能直接用;和 TCP Socket 不是一个层次
  • Node 里:net 模块写 TCP,dgram 写 UDP,WebSocket 用 ws 库或 Node 22 起内置的客户端
  • 长连接要管:心跳、超时、断线重连、消息去重、关闭时释放资源

Socket 可以理解成两台机器之间通信的插座,IP 加端口确定一端,操作系统通过它把数据收发出去。 前端常听到的 WebSocket 和这里的 TCP Socket 不是一回事:TCP Socket 是传输层的字节流,你发两次 hello,对方可能一次收到 hellohello,消息边界得自己定;WebSocket 是跑在 TCP 上的应用层协议,帮你把数据切成一条条消息。Node 里写 TCP 用 net 模块,几行就能起一个服务。

const net = require('net')
net.createServer((socket) => {
  socket.on('data', (buf) => socket.write('echo: ' + buf)) // 原样回显
  socket.on('error', () => socket.destroy())               // 出错要释放
}).listen(9000)
// 另开终端:nc localhost 9000,输入 hi → 收到 echo: hi
@前端进阶之旅: 代码已经复制到剪贴板

理解「Socket」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Socket 这个词在不同语境下指的东西不一样,先分清层次:

  • 操作系统层面,Socket 是进程进行网络通信的接口,由协议、本地 IP 端口、远端 IP 端口确定一条连接。
  • 传输层有 TCP 和 UDP 两种。TCP 面向连接、可靠有序,但它是字节流,没有消息边界;UDP 无连接、不保证送达,但每个数据报是独立的。
  • 应用层的 WebSocket 运行在 TCP 之上,通过 HTTP 的 Upgrade 握手建立连接,之后用帧传输消息,浏览器可以直接使用。Socket.IO 又是在 WebSocket 之上再封装的一层,自带重连、房间、降级轮询,它的客户端不能直接连原生 WebSocket 服务。

在 Node 中对应的模块:

require('net')    // TCP 服务端和客户端
require('dgram')  // UDP
require('ws')     // 第三方 WebSocket 服务端,最常用
// Node 22 起全局有 WebSocket 客户端(和浏览器同一套 API)
@前端进阶之旅: 代码已经复制到剪贴板

写长连接服务时,要额外处理这些问题:

  1. 消息边界:TCP 上要自己做分帧,常见是长度前缀或分隔符。
  2. 存活检测:网络中断时连接可能变成「半开」,要靠心跳或空闲超时发现并清理。
  3. 背压:对方读得慢时,socket.write() 会返回 false,要等 drain 事件再继续写,不然内存会一直涨。
  4. 多实例:内存里的连接表只在本进程有效,多台机器广播要借助 Redis 的发布订阅等外部消息通道。
  5. 重连与去重:断线重连后可能收到重复消息,业务上要带消息 id 做幂等。

面试官追问

追问 1客户端连续发了两条消息,服务端 data 事件只触发一次,内容粘在一起了,为什么?
参考回答

TCP 是字节流,没有消息边界,内核会合并或拆分数据。要自己定协议:按换行分隔,或者每条消息前加 4 字节长度头,服务端攒够长度再切。

追问 2TCP Socket 和 WebSocket 是什么关系?
参考回答

WebSocket 跑在 TCP 之上,先用一次 HTTP 请求升级协议,之后按帧收发消息,帮你处理了边界和掩码。浏览器出于安全不开放裸 TCP,只能用 WebSocket。

追问 3服务端测试跑完了,Node 进程却一直不退出,查什么?
参考回答

多半是还有活着的句柄:没关的连接、没 close() 的 server、没清的定时器。用 why-is-node-running 这类工具能列出来;测试里要在 afterAll 里把连接和服务都关掉。

追问 4长连接服务上线后,连接数只涨不降,怎么回事?
参考回答

客户端异常断网时,服务端未必能马上感知,死连接一直挂着。要加心跳和空闲超时,比如 socket.setTimeout(60000) 后在 timeout 事件里 destroy,或者开 setKeepAlive。

# WebSocket

30 秒速记

  • WebSocket = 一条全双工长连接,服务端能主动推,客户端也能随时发
  • 握手借 HTTP:请求带 Upgrade: websocket,服务端回 101 Switching Protocols,之后就不是 HTTP 了
  • 地址 ws:// / wss://,HTTPS 页面只能连 wss://
  • 浏览器 API:onopen / onmessage / onerror / onclose + send() / close()
  • 工程必备:心跳、断线重连(指数退避)、消息确认去重、多实例靠 Redis 发布订阅广播

WebSocket 是浏览器和服务器之间的一条长连接电话线,接通之后双方随时都能说话,不用像 HTTP 那样一问一答。 它借 HTTP 完成握手,服务端回 101 之后,这条 TCP 连接就切换成 WebSocket 协议,按帧收发消息,每帧头部只有几个字节,比每次带一堆 HTTP 头轻得多。真正难的是连接的生命周期:网断了浏览器不一定马上知道,所以要心跳;断了要自动重连,而且别所有客户端同一秒一起重连。

// Node 服务端,用 ws 库
const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({ port: 8080 })
wss.on('connection', (ws) => {
  ws.on('message', (data) => ws.send(`收到:${data}`))
})
// 浏览器:new WebSocket('ws://localhost:8080').onmessage = e => console.log(e.data)
@前端进阶之旅: 代码已经复制到剪贴板

理解「WebSocket」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 network 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

WebSocket 的连接过程分两步:先是一个普通的 HTTP/1.1 请求,带上升级头;服务端同意后,这条 TCP 连接就改为 WebSocket 协议传输帧。

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
@前端进阶之旅: 代码已经复制到剪贴板

Sec-WebSocket-Accept 是服务端用客户端的 Key 拼上一个固定字符串后做 SHA-1 再 Base64 得到的,用来证明对方真的理解 WebSocket 协议,不是什么安全机制。

浏览器端一个带心跳和重连的最小封装:

function connect(url, onMessage, retry = 0) {
  const ws = new WebSocket(url)
  let timer
  ws.onopen = () => {
    retry = 0
    timer = setInterval(() => ws.send('{"type":"ping"}'), 30000) // 心跳
  }
  ws.onmessage = (e) => onMessage(JSON.parse(e.data))
  ws.onclose = () => {
    clearInterval(timer)
    const delay = Math.min(30000, 1000 * 2 ** retry) + Math.random() * 1000
    setTimeout(() => connect(url, onMessage, retry + 1), delay) // 指数退避重连
  }
}
@前端进阶之旅: 代码已经复制到剪贴板

和其他实时方案的取舍:只需要服务端单向推送(比如通知、AI 流式输出)时,SSE 更简单,走普通 HTTP,自带断线重连;数据变化很少时,轮询也完全够用。WebSocket 适合双向、高频的场景,比如聊天、协同编辑、实时游戏。

面试官追问

追问 1网线拔了,浏览器的 onclose 迟迟不触发,怎么及时发现断线?
参考回答

TCP 没收到断开报文时,浏览器可能很久都认为连接还在。要做应用层心跳:客户端每 30 秒发一个 ping 消息,超过两个周期没收到 pong 就主动 close() 并重连。浏览器 API 发不了协议层的 ping 帧,只能用普通消息模拟。

追问 2服务端发版重启,几万个客户端同时重连,把服务打挂了,怎么避免?
参考回答

重连用指数退避加随机抖动,比如 1s、2s、4s 再各加一个随机值,封顶 30s,别让所有人同一秒冲上来。服务端也可以在关闭时发一个带延迟建议的关闭消息。

追问 3部署了两台 WebSocket 服务,A 上的用户收不到 B 上用户发的群消息,为什么?
参考回答

每台机器只知道连在自己身上的客户端,内存里的房间不跨进程。要用 Redis 的发布订阅或消息队列做中转,每台收到消息都发布出去,所有实例各自推给自己的连接。

追问 4HTTPS 页面连 ws:// 报错了,怎么回事?
参考回答

混合内容,浏览器直接拦截。生产环境统一用 wss://,Nginx 反代时要加 proxy_set_header Upgrade $http_upgrade 和 Connection "upgrade",否则握手过不去。

# 一、WebSocket 解决了什么问题

← Cookie、Session、Token、JWT

fe