Comprehensive Introduction to WebSocket with Heartbeat and Reconnection Implementations
This article explains the WebSocket protocol, its advantages over traditional Ajax polling, typical use cases, the standard WebSocket API, and provides complete JavaScript implementations of a heartbeat base class and an extensible Socket wrapper with reconnection logic.
WebSocket is an HTML5 protocol that provides full‑duplex communication over a single TCP connection, allowing browsers to exchange data with servers more simply and enabling servers to push data to clients without repeated HTTP requests.
Compared with Ajax polling, WebSocket reduces bandwidth consumption and latency because the connection remains open, eliminating the overhead of frequent request headers and improving real‑time responsiveness.
Key characteristics include TCP‑based transport, compatibility with HTTP (default ports 80/443 and HTTP‑based handshake), lightweight framing (header ~2 bytes), support for both text and binary messages, no same‑origin restriction, ws/wss scheme, no built‑in timeout, and an event‑driven model where data is received as frames.
Typical scenarios where WebSocket shines are real‑time communication such as chat rooms, stock price updates, live streaming, desktop sharing, collaborative editing, and any application requiring frequent client‑server interaction.
The standard WebSocket API consists of the WebSocket(url, protocols) constructor, the readyState enumeration (CONNECTING = 0, OPEN = 1, CLOSING = 2, CLOSED = 3), methods like close(code, reason) , send(data) , the bufferedAmount property, and event callbacks onopen , onmessage , onerror , and onclose .
Below is a reusable heartbeat base class that periodically sends a ping message and checks for server responses, automatically resetting timers and allowing custom callbacks:
/**
* 心跳基类
*/
class Heart {
HEART_TIMEOUT = null // 心跳计时器
SERVER_HEART_TIMEOUT = null // 心跳计时器
constructor () {
this.timeout = 5000
}
// 重置
reset () {
clearTimeout(this.HEART_TIMEOUT)
clearTimeout(this.SERVER_HEART_TIMEOUT)
return this
}
/**
* 启动心跳
* @param {Function} cb 回调函数
*/
start (cb) {
this.HEART_TIMEOUT = setTimeout(() => {
cb()
this.SERVER_HEART_TIMEOUT = setTimeout(() => {
cb()
// 重新开始检测
this.reset().start(cb) // this.reset().start(cb())
}, this.timeout)
}, this.timeout)
}
}Building on this, the Socket class extends Heart and adds configurable options, automatic reconnection, and custom event handlers for a full‑featured WebSocket client:
/**
* OPTIONS = {
* url: null, // 链接的通道的地址
* heartTime: 5000, // 心跳时间间隔
* heartMsg: 'ping', // 心跳信息,默认为'ping'
* isReconnect: true, // 是否自动重连
* isRestory: false, // 是否销毁
* reconnectTime: 5000, // 重连时间间隔
* reconnectCount: 5, // 重连次数 -1 则不限制
* openCb: null, // 连接成功的回调
* closeCb: null, // 关闭的回调
* messageCb: null, // 消息的回调
* errorCb: null // 错误的回调
* }
*/
class Socket extends Heart {
ws = null
RECONNEC_TTIMER = null // 重连计时器
RECONNECT_COUNT = 10 // 变量保存,防止丢失
OPTIONS = {
url: null,
heartTime: 5000,
heartMsg: 'ping',
isReconnect: true,
isRestory: false,
reconnectTime: 5000,
reconnectCount: 5,
openCb: null,
closeCb: null,
messageCb: null,
errorCb: null
}
constructor (ops) {
super()
Object.assign(this.OPTIONS, ops)
this.create()
}
/** 建立连接 */
create () {
if (!('WebSocket' in window)) { new Error('当前浏览器不支持,无法使用'); return }
if (!this.OPTIONS.url) { new Error('地址不存在,无法建立通道'); return }
delete this.ws
this.ws = new WebSocket(this.OPTIONS.url)
this.onopen()
this.onclose()
this.onmessage()
}
onopen (callback) {
this.ws.onopen = (event) => {
clearTimeout(this.RECONNEC_TTIMER)
this.OPTIONS.reconnectCount = this.RECONNECT_COUNT
super.reset().start(() => { this.send(this.OPTIONS.heartMsg) })
if (typeof callback === 'function') { callback(event) }
else { (typeof this.OPTIONS.openCb === 'function') && this.OPTIONS.openCb(event) }
}
}
onclose (callback) {
this.ws.onclose = (event) => {
super.reset()
!this.OPTIONS.isRestory && this.onreconnect()
if (typeof callback === 'function') { callback(event) }
else { (typeof this.OPTIONS.closeCb === 'function') && this.OPTIONS.closeCb(event) }
}
}
onerror (callback) {
this.ws.onerror = (event) => {
if (typeof callback === 'function') { callback(event) }
else { (typeof this.OPTIONS.errorCb === 'function') && this.OPTIONS.errorCb(event) }
}
}
// ... other methods like onmessage, send, onreconnect, destroy
destroy () {
super.reset()
clearTimeout(this.RECONNEC_TTIMER)
this.OPTIONS.isRestory = true
this.ws.close()
}
}A simple test using the nodejs‑websocket module demonstrates server‑side heartbeat detection and basic message handling:
const createServer = ws.createServer(function (conn) {
//计算心跳时间
conn.heart_time = 0
let timer = setInterval(() => {
//检查心跳时间
if (conn.heart_time > heart_beat) {
clearInterval(timer);
conn.close()
}
conn.heart_time++
}, 1000)
//uid
let uid = conn.path.split('/')[conn.path.split('/').length - 1] || '0'
conn.uid = uid
console.log(`用户${uid}已经连接`)
conn.sendText(`Hello 用户${uid}!`)
//处理错误事件信息
conn.on('error', function (err) {
console.log('用户' + uid + ' 已经断开连接,错误原因: ' + err)
})
}).listen(7041);For further reading, see the detailed WebSocket guide at ruanyifeng.com and the Node WebSocket implementation on GitHub; the source code is hosted on GitHub and can be installed via npm.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.