> 技术文档 > websocket是什么?怎么用?

websocket是什么?怎么用?


一、什么是websocket?

定义:

webSocket是一种在单个TCP连接上进行全双工通信的协议。websocket使得客户端服务器之间的数据交换变得更加简单,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,属于服务器推送技术的一种。

在Websocket API中,浏览器和服务器只需要完成一次握手,两者之间可以创建持久性的连接,并进行双向数据传输。websocket本质上是一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。

特点:

1.建立在TCP协议之上,服务器端的实现比较容易

为什么选择TCP?

可靠性:TCP提供可靠的传输,确保数据包按顺序到达且无丢失,而且TCP具有错误恢复机制,可重传丢失的包和校验数据完整性;

面向连接的通信:Websocket设计为长连接,TCP的面向连接(通过三次握手建立连接,四次挥手关闭连接)特性支持长期稳定的通信通道;TCP的全双工模式为websocket的双向通信提供底层支持。

与HTTP握手兼容:Websocket握手阶段使用HTTP协议(通过Upgrade头切换协议),而HTTP本身基于TCP,使得Websocket无缝融入现有的Web生态。

补充说明:为何不选择UDP?

虽然UDP具有低延迟和无连接特性,但其不可靠性(丢包、乱序)不适合WebSocket的典型使用场景。若基于UDP,WebSocket需自行处理重传、排序等问题,增加复杂性,违背其设计初衷(简化实时通信)。

2.与HTTP协议有着良好的兼容性。默认端口也是80(非加密)和443(加密),并且握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器。

当浏览器向服务器发起websocket连接时,会先发送http请求(带有Upgrade头)进行握手,服务器响应101 Switching Protocols状态码,表示同意将连接升级为Websocket协议,之后双方可以基于Websocket进行通信,而这个过程对于浏览器和中间代理服务器来说,就像处理普通的HTTP请求一样,只是在握手成功后才切换到Websocket协议进行数据传输。

3.没有同源限制,客户端可以与任意服务器通信

4.websocket的数据帧结构相对简单,它只包含一些必要的头部信息和数据内容,不像HTTP协议那样有大量的头部字段和复杂的消息格式。这使得在数据传输过程中,websocket能减少数据冗余,减低传输开销,提高通信效率

5.websocket可以发送文本,也能直接传输二进制数据,如图片、音频、视频等

6.WebSocket 使用ws作为协议标识符来标识非加密的 WebSocket 连接,而对于加密的 WebSocket 连接则使用wss

ws://example.com:8080/wswss://secure.example.com/ws

网络协议连接方式http/https/ws/wss比较

HTTP协议

客户端基于TCP/IP协议与服务器建立连接;连接建立后,客户端发送HTTP请求,服务器接收请求,以请求URL确定处理方式并向客户端返回响应内容。

HTTPS协议:

  • 客户端和服务器先通过TCP协议建立连接,确定双方能可靠传输数据。
  • TLS/SSL握手
  • 握手完成,建立安全通道,之后客户端和服务器间数据传输都经 TLS/SSL 加密,保证数据机密性、完整性 ,防止数据被窃取、篡改。

补充TLS/SSL握手过程

1.建立TCP连接后,客户端向服务器发送握手消息,其中包含它所支持的加密算法和以一个随机数。

2.服务器收到消息后,服务器接收到客户端的信息后,从客户端提供的加密算法列表中选择一个它也支持的算法。将包含公共密钥的证书(包含服务器的公钥)及自身的随机数发送给客户端。(握手消息)

3.客户端验证证书(检查签发机构、证书是否过期),抽取公共密钥,生成 pre_master_secret 随机密码串,用服务器公钥加密后发送给服务器(只有持有对应私钥的服务器才能解密 pre_master_secret )

4.双方依据 pre_master_secret 及各自随机数独立计算出用于后续数据加密的加密密钥,使用消息认证码(MAC)计算 MAC 密钥

5.客户端和服务器分别使用计算得到的 MAC 密钥对握手消息计算 MAC 值,并发送给对方;对方收MAC值,使用相同的密钥对收到的握手消息计算MAC值,对比之后如果相同说明双方的MAC密钥一致,握手成功。

6.此时,双方可以开始使用协商好的密钥进行安全的数据传输,因为双方的密钥一致,并且能够验证消息的完整性和真实性,所以可以保证数据在传输过程中不被篡改,且确实来自合法的通信对方

WS:

  • 客户端向服务器发送特殊 HTTP 请求(含 WebSocket 相关头部字段,如 Upgrade: websocket ,表示要升级协议),请求建立 WebSocket 连接。
  • 服务器接收请求,若支持 WebSocket 协议,回复 101 Switching Protocols 状态码,确认协议升级,双方切换到 WebSocket 协议通信。
  • 握手成功,在 TCP 连接基础上建立持久连接,后续双方可随时双向通信,无需重复建立连接。
  • 连接建立后,客户端和服务器可互相发送文本或二进制数据,实现实时通信,如聊天消息发送、实时数据推送。

WSS:

  • 同 HTTPS,先通过 TCP 协议建立基础连接。
  • 与 HTTPS 的 TLS/SSL 握手类似,客户端和服务器协商加密算法、交换密钥等,建立安全通道。
  • TLS/SSL 握手成功,基于已建立的安全通道,进行 WebSocket 握手,过程同 WS 协议的握手 ,建立 WebSocket 连接。
  • 通过已建立的加密 WebSocket 连接,客户端和服务器进行双向数据传输,数据在传输中被加密,保障安全。

二、为什么需要WebSocket

HTTP协议有一个缺陷:通信只能由客户端发起,不具备服务器推送能力。这种单向请求的特点,客户端获取服务器消息只能使用轮询:每隔一段时间,客户端发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室,客户端与服务器交互频繁,如果使用HTTP协议,轮询方式导致效率低,非常浪费资源。

  • 短轮询:客户端定时向服务器发送 HTTP 请求询问是否有新消息。比如每 5 秒发一次请求,不管有无新消息都得发。若服务器没新消息,这次请求就白白消耗了网络带宽和服务器资源;频繁请求还会增加客户端设备的功耗和性能开销。
  • 长轮询:客户端向服务器发送 HTTP 请求来获取消息。如果服务器当时没有新消息可供发送,它不会像传统的 HTTP 请求那样立即返回一个没有消息的响应,而是将这个请求保持挂起状态,也就是让连接一直处于打开状态。服务器会等待,直到有新消息产生或者达到了预先设定的超时时间,才会向客户端发送响应。这样做的目的是为了让客户端能够尽可能及时地获取到新消息,而不需要像短轮询那样频繁地发送请求去询问是否有新消息。

在websocket协议出现前,基于HTTP协议实现Web应用与服务端双通道通信需不停轮询,引发服务端需维持大量客户端连接,以及轮询请求产生高开销(如携带多余header致无用的数据传输)等问题。

为了解决这些问题,websocket协议由此而生,于2011年被IETF定为标准RFC6455,并被RFC7936所补充规范。并且在HTML5标准中增加了有关WebSocket协议的相关api,所以只要实现了HTML5标准的客户端,就可以与支持WebSocket协议的服务器进行全双工的持久通信了。

三、WebSocket与HTTP的区别

相同点::都是一样基于TCP的,都是可靠性传输协议,都是应用层协议

联系:WebSocket在建立握手时,数据是通过HTTP传输的。但是握手之后,数据传输是通过ws协议的

根据以上图我们来说明一下其区别:

1.连接方式

HTTP:连接相对短暂,客户端向服务器发送请求,服务器响应请求后连接即结束。即使在 HTTP/1.1 及后续版本中支持持久连接,也主要是为了在同一连接上进行多个请求 / 响应,本质上还是基于请求 - 响应模式。

WebSocket:在客户端和服务器之间建立持久的双向连接,连接一旦建立,双方可随时主动发送数据,实现实时通信。

2.数据传输

HTTP:请求和响应数据格式较为复杂,包含大量的头部信息等,每次传输可能会携带很多冗余数据。数据传输是单向的,由客户端发起请求,服务器进行响应。

WebSocket:数据格式相对轻量,传输更加高效。而且支持全双工通信,客户端和服务器可以同时发送和接收数据,在实时性要求高的场景(如在线游戏、实时聊天等)中表现更优。

3.建立连接的方式

HTTP:客户端通过向服务器发送 HTTP 请求来建立连接,服务器根据请求的 URL、方法等信息进行相应的处理并返回响应。

WebSocket:连接建立需要通过 HTTP 协议进行握手。客户端发送带有特殊请求头的 HTTP 请求到服务器,服务器响应表示同意升级连接为 WebSocket 连接,连接建立后就可以使用 WebSocket 协议进行通信,不再依赖 HTTP 协议的请求 - 响应模式。

4.协议特点

HTTP:是无状态协议,服务器不保留客户端的状态信息,每个请求都是独立的,这使得服务器的实现相对简单,但在处理一些需要状态管理的业务逻辑时,需要额外的机制(如会话管理、Cookie 等)来维护状态。

websocket:连接建立后,双方可以在同一连接上持续进行数据交互,具有状态性,更适合处理需要保持上下文信息的业务场景。

5.应用场景:

HTTP:适用于获取静态资源(如网页、图片、文件等)以及进行简单的信息查询和提交等场景,例如浏览普通的新闻网站、提交表单数据等。

WebSocket:主要用于需要实时交互和双向通信的场景,如在线聊天、实时监控、股票行情推送、多人协作编辑等,能为用户提供更流畅、实时的体验。

四、websocket协议的原理

与http协议一样,websocket协议也需要通过已建立的TCP连接来传输数据,具体实现上是通过http协议建立通道,然后在此基础上用真正的websocket协议进行通信。

1.先建立TCP连接

2.握手过程

客户端发起请求

//get是http请求方法,表示从服务器的/chart路径请求目标资源;HTTP/1.1表示使用的HTTP协议版本GET /chat HTTP/1.1//指定请求目标服务器的主机名和端口号(若未明确指定端口,对于 HTTP 默认 80 端口,HTTPS 默认 443 端口)Host: server.example.com//告诉服务器客户端希望将当前的HTTP连接升级为Websocket连接Upgrade: websocket//配合 “Upgrade” 字段使用,表明客户端希望服务器在处理完本次请求后,将连接升级为 “Upgrade” 字段指定的协议(此处为 WebSocket)。它表示连接的属性要进行改变,从 HTTP 协议切换到 WebSocket 协议。Connection: Upgrade//由客户端随机生成的Base64编码的密钥。服务器接收到密钥后,使用特定算法与固定字符串拼接后进行哈希计算,再将结果进行Base64编码返回给客户端//客户端收到响应密钥后使用与服务器端相同的算法对其处理,客户端将自己计算出的结果与服务器返回的字段比较;相对则通过,否则拒绝连接;Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==//客户端向服务器表明自己支持的子协议列表。“chat” 和 “superchat” 就是这里列举的两个子协议,服务器可以从中选择一个双方都支持的子协议进行后续通信。如果服务器不支持任何一个,它可以选择不接受这个 WebSocket 连接,或者忽略该字段使用默认的 WebSocket 通信方式。Sec-WebSocket-Protocol: chat, superchat//用于指定客户端使用的 WebSocket 协议版本。服务器会检查该版本号,如果服务器支持这个版本,就可以继续进行连接升级流程;如果不支持,服务器可能会返回错误信息,拒绝升级连接。Sec-WebSocket-Version: 13// /发起这个 WebSocket 连接请求的网页所在的域Origin: http://example.com

然后服务器返回以下字段,表示已经接收到请求,成功建立Websocket

//响应行,101状态码表示服务器同意将连接升级为新的协议。HTTP/1.1 101 Switching Protocols//确认将连接升级为 WebSocket 协议。Upgrade: websocket//确认改变连接的协议。Connection: Upgrade//服务器根据客户端的Sec-WebSocket-Key计算得到的 Base64 编码的响应密钥,用于验证连接的合法性。Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=//服务器选择的子协议。Sec-WebSocket-Protocol: chat

3.数据传输阶段

握手成功后,客户端和服务器之间建立websocket连接,可以进行双向传输数据,websocket数据帧是websocket协议中用于传输数据的基本单位,格式如下:

 0  1  2  3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64)  ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +|  Payload Data continued ... |+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+|  Payload Data continued ... |+---------------------------------------------------------------+
  • FIN(1 bit):表示这是否是消息的最后一个数据帧。如果为 1,则表示是最后一个数据帧;如果为 0,则表示还有后续数据帧。
  • RSV1、RSV2、RSV3(各 1 bit):保留位,用于扩展 WebSocket 协议,默认值为 0。
  • opcode(4 bits):表示数据帧的类型,常见的类型有:
    • 0x0:表示延续帧,用于分割大消息。
    • 0x1:表示文本帧,数据为 UTF-8 编码的文本。
    • 0x2:表示二进制帧,数据为二进制数据。
    • 0x8:表示关闭帧,用于关闭 WebSocket 连接。
    • 0x9:表示 Ping 帧,用于检测连接是否存活。
    • 0xA:表示 Pong 帧,是对 Ping 帧的响应。
  • MASK(1 bit):表示数据是否进行了掩码处理。客户端发送的数据帧必须进行掩码处理,服务器发送的数据帧不能进行掩码处理。
  • Payload length(7 bits、7+16 bits 或 7+64 bits):表示数据帧的有效负载长度。如果值在 0 - 125 之间,则表示实际的负载长度;如果值为 126,则接下来的 16 位表示负载长度;如果值为 127,则接下来的 64 位表示负载长度。
  • Masking-key(0 或 32 bits):如果MASK为 1,则包含一个 32 位的掩码密钥,用于对数据进行掩码处理。
  • Payload Data:实际传输的数据。

4.连接关闭阶段:

当客户端或服务器需要关闭 WebSocket 连接时,会发送一个关闭帧(opcode 为0x8)。关闭帧可以包含一个状态码和一个可选的关闭原因。对方收到关闭帧后,会发送一个确认关闭帧进行响应,然后双方关闭连接。以下是一个示例关闭帧的状态码和含义:

  • 1000:表示正常关闭。
  • 1001:表示端点离开,例如服务器关闭或浏览器关闭页面。
  • 1002:表示由于协议错误而关闭。
  • 1003:表示由于不支持的数据类型而关闭。
  • 1005:表示没有状态码的关闭。
  • 1006:表示异常关闭,没有发送或接收关闭帧。

五、websocket的优缺点

优点:

  • WebSocket协议一旦建议后,互相沟通所消耗的请求头是很小的
  • 服务器可以向客户端推送消息了

缺点:

  • 少部分浏览器不支持,浏览器支持的程度与方式有区别(IE10)

六、websocket应用场景

  • 即时聊天通信:用户在连天客户端(如网页聊天窗口)输入消息后,客户端将消息通过已经建立的websocket连接发送给服务器。服务器收到消息后,根据消息的目标(单聊时的特定用户、群聊时的群组),再通过 WebSocket 将消息推送给相应的接收方客户端。
  • 多玩家游戏:以在线多人对战游戏为例,玩家操作游戏角色(如移动、攻击等)时,客户端将操作指令通过 WebSocket 发送给游戏服务器。服务器实时更新游戏世界状态(如角色位置、生命值变化),再通过 WebSocket 把最新状态推送给所有参与游戏的玩家客户端,客户端据此更新游戏画面。
  • 在线协同编辑:多个用户同时打开在线文档编辑页面,客户端与服务器建立 WebSocket 连接。当某个用户进行编辑操作(如输入文字、修改格式),操作数据会立即通过 WebSocket 发送给服务器。服务器整合这些操作,更新文档状态后,再通过 WebSocket 将新的文档内容推送给其他正在编辑的用户客户端,实现文档内容实时同步。
  • 实时地图位置:以共享出行软件为例,用户开启软件后,手机的定位系统获取用户位置信息,通过客户端的 WebSocket 连接将位置数据发送给服务器。服务器实时更新用户在地图上的位置,并通过 WebSocket 把附近其他用户(如拼车乘客、司机)的位置信息推送给该用户客户端,客户端在地图上显示出各方位置。

不能使用websocket的场景:

如果我们需要通过网络传输的任何实时更新或连续数据流,则可以使用WebSocket。如果我们要获取旧数据,或者只想获取一次数据供应用程序使用,则应该使用HTTP协议,不需要很频繁或仅获取一次的数据可以通过简单的HTTP请求查询,因此在这种情况下最好不要使用WebSocket

七、websocket心跳机制和断线重连

  • onopen:在 WebSocket 连接成功建立之后调用。
  • onmessage:当客户端接收到服务器发送的消息时调用。
  • onclose:在 WebSocket 连接关闭时调用。
  • onerror:在 WebSocket 连接出现错误时调用。

客户端

1.如何与服务器进行通信:调用方法this.send({type:\'消息类型\',message:\'消息内容\'})

2.心跳机制:心跳机制是为了实时检测连接的状态,客户端每隔一段时间发送ping包,如果能够接收到服务器的pong响应,说明连接正常;否则说明连接出现问题:可能是断网了

怎么做心跳机制?

  • 设置心跳定时器heartbeatTimer,用于每隔一段事件发送一次心跳包;设置pingTimeout存储ping的超时定时器,如果规定时间内没有收到pong响应说明服务器有问题则主动断开ws连接
  • 封装startHeartbeat开启心跳,当连接成功建立时在onopen里开启心跳
//开启心跳检测startHeartbeat(){ //心跳定时器 this.heartbeatTimer=setInterval(()=>{ //发送心跳包 this.send({type:ping}) //如果5s还没收到响应断开连接 this.pingTimeout=setTimeout(()=>{ if(this.ws.readyState==WebSocket.OPEN){ this.ws.close() } },5000) },25000)}
  • 封装stopHeartbeat停止心跳,在连接关闭的时候停止心跳
//停止心跳 stopHeartbeat() { console.log(\"停止心跳\") clearInterval(this.heartbeatTimer); if (this.pingTimeout) { clearTimeout(this.pingTimeout); } }

3.重连机制:如果不是客户端主动断开ws连接,由于网络等原因被迫断开的连接调用重连机制

  • 设置重连定时器reconnectTimer,使用指数退避策略实施重连,随着重连失败次数增多重连延时时间延长,节省网络资源;记录重连尝试次数reconnectAttempts和最大重连尝试次数maxReconnectAttempts停止重连
  • 封装重连机制
//计划重连scheduleReconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.log(\'已达最大重连次数,停止重试\') return } //指数退避策略 const delay = Math.pow(2, this.reconnectAttempts) * 1000 this.reconnectTimer = setTimeout(() => { console.log(\'尝试重连\', this.reconnectAttempts) this.reconnectAttempts++ this.connect() }, delay)}

完整代码如下:

//模拟客户端websocketexport class webSocketClient{ constructor(){ //websocket实例 this.ws=null, //存储心跳定时器 this.heartbeatTimer=null, //心跳响应超时定时器 this.pingTimeout=null, //重连次数 this.reconnectAttemps=0, //重连最大次数 this.maxReconnectAttemps=5 //重连定时器 this.reconnectTimer=null } //初始化连接 connect(){ //这里假如一个用户只用一个ws连接 if(this.ws) this.ws.close() //初始化一个ws连接 this.ws=new WebSocket(\'握手的接口\') //连接成功回调 this.ws.onopen=()=>{ console.log(\"ws连接成功\") //初始化重连次数,开始心跳 this.reconnectAttemps=0 this.startHeartbeat() } //接收服务器消息的回调 this.ws.onmessage=(event)=>{ const data=JSON.parse(event.data) if(data.type==\'pong\'){ console.log(\"心跳正常\") if(this.pingTimeout){  clearTimeout(this.pingTimeout)  this.pingTimeout=null } }else{ //处理业务消息 } } this.ws.onerror=(error)=>{ console.log(\'ws连接出错\',error) } //连接关闭触发回调 this.ws.onclose=(event)=>{ console.log(\'ws连接关闭的原因\',event.reason) //停止心跳检测 this.stopHeartbeat() //如果是异常关闭,非主动关闭,则尝试重连 if(!event.wasClean){ console.log(\"重连\") this.scheduleReconnect() } } } //开启心跳检测 startHeartbeat(){ //心跳定时器 this.heartbeatTimer=setInterval(()=>{ //发送心跳包 this.send({type:ping}) //如果5s还没收到响应断开连接 this.pingTimeout=setTimeout(()=>{ if(this.ws.readyState==WebSocket.OPEN){  this.ws.close() } },5000) },25000) } //停止心跳 stopHeartbeat(){ console.log(\'停止心跳\') clearInterval(this.heartbeatTimer) if(this.pingTimeout){ clearTimeout(this.pingTimeout) } } //send封装 send(message){ if(this.ws.readyState==WebSocket.OPEN){ this.ws.send(JSON.stringify(message)) } } //重连 scheduleReconnect(){ if(this.reconnectAttemps>=this.maxReconnectAttemps){ console.log(\"以达到最大重连次数,停止重连\") return } //指数退避策略 const delay=Math.pow(2,this.reconnectAttemps)*1000 this.reconnectTimer=setTimeout(()=>{ this.reconnectAttemps++ this.connect() },delay) }}//记得配置代理的时候ws:true

服务端:

1.当有新的客户端连接到服务器时触发连接对应的回调,wss.on(\'connection\',()=>{})

2.验证客户端的携带的token是否合法,如果合法才做出相应的处理,否则关闭连接

  • 绑定用户连接userConnections,将用户id与ws连接相对应存储起来,后续如果要给客户端发送消息时,找到相对应的用户的连接,可以调用send方法给指定的用户id发送消息

验证原理

JSON Web Token 是一种用于在网络应用中安全传输信息的开放标准(RFC 7519)。一个 JWT 通常由三部分组成,用点(.)分隔,分别是头部(Header)、载荷(Payload)和签名(Signature)。验证 JWT 的核心就是验证签名的有效性,确保 JWT 在传输过程中没有被篡改。

验证过程

  1. 解析 JWT:首先,jwt.verify 方法会将传入的 JWT 字符串按照点(.)进行分割,得到头部、载荷和签名三部分。
  2. 验证签名:使用相同的签名算法(在头部中指定)和密钥(JWT_SECRET)对头部和载荷进行签名计算,得到一个新的签名。然后将新计算的签名与 JWT 中的签名进行比较,如果两者相同,则说明签名有效,JWT 在传输过程中没有被篡改。
  3. 验证过期时间:如果 JWT 的载荷中包含 exp(过期时间)字段,jwt.verify 方法会检查当前时间是否已经超过了 exp 指定的时间。如果超过了,则说明 JWT 已经过期,验证失败。
  4. 返回结果:如果签名有效且没有过期,验证成功,回调函数的 err 参数为 nulldecoded 参数包含解码后的载荷信息;如果签名无效或者 JWT 已经过期,验证失败,回调函数的 err 参数包含相应的错误信息,decoded 参数为 undefined

验证合法的条件

验证合法需要满足以下两个条件:

  1. 签名有效:新计算的签名与 JWT 中的签名相同,说明 JWT 在传输过程中没有被篡改。
  2. 未过期:如果 JWT 的载荷中包含 exp 字段,当前时间必须在 exp 指定的时间之前,说明 JWT 还在有效期内。

    3.服务器主动检测心跳

    上面的客户端我们自定义了一个心跳机制,服务端我们用ws.ping()触发websocket底层的心跳机制

    这里使用了ws的isAlive属性记录客户端心跳状态,true表示客户端存活,false表示客户端离线了

    //全局心跳检测(服务器主动检测,如果客户端超时回应pong断开连接)setInterval(()=>{ wss.clients.forEach((ws)=>{ if(ws.isAlive==false){ console.log(\'心跳超时,关闭连接\') return ws.terminate()//强制断开 } ws.isAlive=false // 该方法是 WebSocket 协议的一部分,用于发送一个 ping 帧到客户端。 // 当客户端收到 ping 帧后,协议层会自动返回一个 pong 帧作为响应。 // 这个过程在 WebSocket 协议内部处理,不需要你在客户端手动监听或回复 pong。 ws.ping() })},30000)

    完整代码如下:

    const http=require(\'http\')const WebSocket=require(\'ws\')const express=require(\'express\')//创建应用const app=express()const server=http.createServer(app)///创建ws服务器实例const wss=new WebSocket({noServer:true})//握手阶段,将http连接升级为ws连接//request是 http.IncomingMessage 实例,代表客户端的http请求,包按请求头headers,请求方法,url等信息//socket是net.Socket 实例,代表与客户端之间底层的TCP套接字连接,你可以借助这个对象对底层的网络连接进行控制,比如读和写数据socket.write()向客户端发送数据//head是 Buffer 类型的对象,包含了客户端发送的额外数据,这些数据是在升级请求中紧跟在 HTTP 头之后的部分。server.on(\'upgrade\',(request,socket,head)=>{ //handleUpgrade 方法的作用是把普通的 HTTP 连接升级为 WebSocket 连接。升级成功后调用回调 //回调函数中的 ws 是一个 ws.WebSocket 实例,代表与客户端建立的 WebSocket 连接 wss.handleUpgrade(request,socket,head,(ws)=>{ wss.emit(\'connection\',ws,request) })})wss.on(\'connection\',(ws,req)=>{ console.log(\"处理客户端的ws连接\") //验证用户身份 const token=new URL(req.url,\'baseURL\').searchParams.get(\'token\') jwt.verify(token,JWT_SECRET,(err,decoded)=>{ if(err){ console.log(\'无效token,关闭连接\') ws.close(4003,\'Athentication Failed\') return } //这里可以绑定用户信息等逻辑操作 //处理心跳,自定义isAlive将客户端的状态设置为存活 ws.isAlive=true //监听pong消息,这里是响应服务器触发底层的心跳机制,客户端会自动返回pong ws.on(\'pong\',()=>{ ws.isAlive=true }) //处理消息 ws.on(\'message\',(data)=>{ try{ const message=JSON.parse(data) //这里是响应客户端自定义的心跳机制 if(message.type==\'ping\'){  ws.send(JSON.stringify({type:\'pong\'})) }else{  //处理业务消息 } }catch(err){ ws.send(\'{\"type\":\"error\",\"message\":\"消息格式错误\"}\') } }) })})//全局心跳检测(服务器主动检测,如果客户端超时回应pong断开连接)setInterval(()=>{ wss.clients.forEach((ws)=>{ if(ws.isAlive==false){ console.log(\'心跳超时,关闭连接\') return ws.terminate()//强制断开 } ws.isAlive=false // 该方法是 WebSocket 协议的一部分,用于发送一个 ping 帧到客户端。 // 当客户端收到 ping 帧后,协议层会自动返回一个 pong 帧作为响应。 // 这个过程在 WebSocket 协议内部处理,不需要你在客户端手动监听或回复 pong。 ws.ping() })},30000)

    参考:

    一文吃透 WebSocket 原理 刚面试完,趁热赶紧整理因为项目中使用到了 WebSocket ,面试官在深挖项目经验 - 掘金