WebSocket
什么是WebSocket
WebSocket
是一种协议,用于在Web
应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket
协议最初由W3C
开发,并于2011年成为标准。
WebSocket
的生命周期
WebSocket
生命周期描述了 WebSocket
连接从创建到关闭的过程。一个 WebSocket
连接包含以下四个主要阶段:
- 连接建立阶段(
Connection Establishment
): 在这个阶段,客户端和服务器之间的WebSocket
连接被建立。客户端发送一个WebSocket
握手请求,服务器响应一个握手响应,然后连接就被建立了。 - 连接开放阶段(
Connection Open
): 在这个阶段,WebSocket
连接已经建立并开放,客户端和服务器可以在连接上互相发送数据。 - 连接关闭阶段(
Connection Closing
): 在这个阶段,一个WebSocket
连接即将被关闭。它可以被客户端或服务器发起,通过发送一个关闭帧来关闭连接。 - 连接关闭完成阶段(
Connection Closed
): 在这个阶段,WebSocket
连接已经完全关闭。客户端和服务器之间的任何交互都将无效。
需要注意的是,
WebSocket
连接在任何时候都可能关闭,例如网络故障、服务器崩溃等情况都可能导致连接关闭。因此,需要及时处理WebSocket
连接关闭的事件,以确保应用程序的可靠性和稳定性。
WebSocket
的优势和劣势
WebSocket
的优势包括:
- 实时性: 由于
WebSocket
的持久化连接,它可以实现实时的数据传输,避免了Web应用程序需要不断地发送请求以获取最新数据的情况。 - 双向通信:
WebSocket
协议支持双向通信,这意味着服务器可以主动向客户端发送数据,而不需要客户端发送请求。 - 减少网络负载: 由于
WebSocket
的持久化连接,它可以减少HTTP请求的数量,从而减少了网络负载。
WebSocket
的劣势包括:
- 需要浏览器和服务器都支持:
WebSocket
是一种相对新的技术,需要浏览器和服务器都支持。一些旧的浏览器和服务器可能不支持WebSocket
。 - 需要额外的开销:
WebSocket
需要在服务器上维护长时间的连接,这需要额外的开销,包括内存和CPU。 - 安全问题: 由于
WebSocket
允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。
WebSocket
封装(重试、心跳)
const HEART_PING = "HEART_PING";
const HEART_PONG = "HEART_PONG";export default class MySocket {constructor(option) {this.websock = null;// 固定的WebSocket地址:此处是从env文件中读取socket地址,可以自行从其他config文件中读取或直接写死// 如需使用动态WebSocket地址,请自行作ajax通讯后扩展this.ws_url = option.ws_url || process.env.VUE_APP_API_SOCKET_URL || "";// 心跳发送频率this.hearbeat_interval = option.hearbeat_interval || 10000;// 是否自动重连this.is_reonnect = option.is_reonnect === false ? false : true;// 重连次数this.reconnect_count = option.reconnect_count || 1000;// 已发起重连次数this.reconnect_current = option.reconnect_current || 1;// 重连timer// this.reconnect_timer: null// 重连频率this.reconnect_interval = option.reconnect_interval || 1000;this.handleError = option.handleError || null;this.handleClose = option.handleClose || null;this.handleOpen = option.handleOpen || null;this.handleTip = option.handleTip || null;this.handleReceive = option.handleReceive || null;}// 开启标识socket_open = false;// 心跳timerhearbeat_timer = null;// 重连timerreconnect_timer = null;/*** 初始化连接*/init() {if (!('WebSocket' in window)) {console.log('浏览器不支持WebSocket');if (this.handleTip) {this.handleTip('浏览器不支持WebSocket');}return '浏览器不支持WebSocket';}// 已经创建过连接不再重复创建if (this.websock) {return this.websock;}if ((this.reconnect_current != 1 && this.is_reonnect) ||this.reconnect_current == 1) {this.websock = new WebSocket(this.ws_url);this.websock.onmessage = (e) => {if (e.data == HEART_PING) {return false;}if (this.handleReceive) {this.handleReceive(e);}};// 关闭连接this.websock.onclose = (e) => {console.log('连接已断开');clearInterval(this.hearbeat_interval);this.socket_open = false;this.websock = '';if (this.handleClose) {this.handleClose();}// 需要重新连接if (this.is_reonnect) {this.reconnect_timer = setTimeout(() => {// 超过重连次数if (this.reconnect_current > this.reconnect_count) {clearTimeout(this.reconnect_timer);return;}// 记录重连次数this.reconnect_current++;this.reconnect();}, this.reconnect_interval);}};// 连接成功this.websock.onopen = () => {console.log('连接成功');this.socket_open = true;this.reconnect_current = 1;// 开启心跳this.heartbeat();if (this.handleOpen) {this.handleOpen();}};// 连接发生错误this.websock.onerror = () => {console.log('WebSocket连接发生错误');clearInterval(this.hearbeat_interval);this.socket_open = false;this.websock = '';if (this.handleError) {this.handleError();}if (this.is_reonnect) {this.reconnect_timer = setTimeout(() => {// 超过重连次数if (this.reconnect_current > this.reconnect_count) {clearTimeout(this.reconnect_timer);return;}// 记录重连次数this.reconnect_current++;this.reconnect();}, this.reconnect_interval);}};}}/*** 发送消息* @param {*} data 发送数据* @param {*} callback 发送后的自定义回调函数*/send(data, callback = null) {// 开启状态直接发送if (this.websock.readyState === this.websock.OPEN) {if (this.websock) {this.websock.send(data);}if (callback) {callback();}// 正在开启状态,则等待1s后重新调用} else if (this.websock.readyState === this.websock.CONNECTING) {setTimeout(() => {this.send(data, callback);}, 1000);// 未开启,则等待1s后重新调用} else {this.init();setTimeout(() => {this.send(data, callback);}, 1000);}}/*** 心跳*/heartbeat() {if (this.hearbeat_timer) {clearInterval(this.hearbeat_timer);}this.hearbeat_timer = setInterval(() => {this.send(HEART_PONG);}, this.hearbeat_interval);}/*** 主动关闭连接*/initiativeClose() {console.log('主动断开连接');clearInterval(this.hearbeat_timer);this.is_reonnect = false;if (this.websock) {this.websock.close();}}/*** 重新连接*/reconnect() {if (this.websock && this.socket_open) {this.websock.close();}this.init();}
}