一、前言
在我们了解websocket之前,不妨先想想这几个问题:
- websocket是什么?
- websocket有什么好处和特点?
- 为什么要用到websocket?
- 什么情况下会用到websocket?
好了,带着这几个疑问一起来了解一下websocket。
二、websocket简介
2.1 什么是websocket
WebSocket是HTML5开始提供的一种基于TCP的协议,用于在客户端和服务器之间建立持久连接,实现实时通讯的功能。
早期,客户端如果想实时拿到浏览器的最新数据就必须要通过发送http请求定时做轮询。每隔一段时间去向浏览器请求最新数据,这样大大的消耗了服务器的资源。
当使用了websocket与服务器建立连接,就不需要反复去轮询请求数据;当有最新消息时服务器会返回给客户端,而websocket可以立马监听到服务器的返回消息。
2.2 websocket的特点
2.2.1 优点
- 可以说实现连接的持久化,使客户端与服务器可以实时双向通讯;
- 减少了HTTP请求,大大减少了服务器的资源消耗,降低了网络负载;
- 没有同源限制,客户端可以与任意服务器通信;
- 协议标识符为ws,如果是加密的话就是wss;
2.2.2 缺点
- 有兼容问题,有一些老版本的浏览器不支持,同时有一些网络代理或防火墙会阻止连接;
- 缺乏安全策略,前面讲过没有同源策略的限制(这个需要注意);
- 对网络要求比较高,如果网络波动频繁可以回导致经常断联(导致断联原因不好排查);
- 需要保持长连接也会占用服务器的较多资源;
- 不适合大文件传输,websocket协议发送数据包不能超过2GB;
2.3 使用场景
websocket的主要优点就是保持客户端与服务端双向实时通讯,所以他主要的引用场景在于:
- 在线聊天室、游戏消息推送;
- 多媒体对话,例如视频会议等;
- 网页更新实时的数据流,股票价格波动、票房分析等;
三、websocket对象详解
WebSocket 对象提供了用于创建和管理 WebSocket]连接,以及可以通过该连接发送和接收数据的 API。
3.1 实例属性
通过使用WebSocket()构造函数来实例化一个WebSocket对象。
3.1.1 url
Websocket的绝对路径(只读)
3.1.2 readyState
WebSocket当前连接的状态(只读)
- WebSocket.CONNECTING:正在连接中
- WebSocket.OPEN:已经连接可以通信
- WebSocket.CLOSING:连接正在关闭
- WebSocket.CLOSED:连接已关闭或者没有连接成功
3.1.3 protocol
protocol是个只读属性,用于返回服务器端选中的子协议的名字;这是一个在创建 WebSocket对象时,在参数 protocols 中指定的字符串,当没有已建立的链接时为空串。
3.2 实例方法
3.2.1 close()
WebSocket.close() 方法关闭 WebSocket连接或连接尝试(如果有的话)。如果连接已经关闭,则此方法不执行任何操作。
3.2.2 send()
WebSocket.send() 方法将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount的值。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
3.3 事件监听回调
3.3.1 close
监听将在 WebSocket 连接的readyState变为 CLOSED时被调用
3.3.2 error
当websocket的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个error事件将被引发。
3.3.3 message
message 事件会在 WebSocket 接收到新消息时被触发。
3.3.4 open
监听将在 WebSocket 连接的readyState变为 CONNECTING时被调用
四、封装WebSocket工具函数
export class WebSocketClient {
url = '';
socket = null;
// #计时器id
heartbeatTimer = undefined;
// 是否彻底关闭
stopWebSocket = false;
// 消息列表
msgList = [];
// 事件监听对象
eventListenerInfo = {};
// 构造函数
constructor(url) {
this.url = url;
}
// 添加事件监听
addEventListener(type, listener) {
if (!this.eventListenerInfo[type]) {
this.eventListenerInfo[type] = [];
}
if (this.eventListenerInfo[type].indexOf(listener) === -1) {
this.eventListenerInfo[type].push(listener);
}
}
// 删除事件监听
removeEventListener(type) {
this.eventListenerInfo[type] = [];
}
// 监听事件绑定
dispatchEvent(type, data) {
const listenerArray = this.eventListenerInfo[type] || [];
if (listenerArray.length === 0) return;
listenerArray.forEach(listener => {
listener.call(this, data);
});
}
// 监听回调
onopen(callBack) {
this.addEventListener('open', callBack);
}
onmessage(callBack) {
this.addEventListener('message', callBack);
}
onclose(callBack) {
this.addEventListener('close', callBack);
}
onerror(callBack) {
this.addEventListener('error', callBack);
}
// 消息列表增加
addMsgList(list) {
this.msgList = [...this.msgList, ...list];
}
// 批量发送消息
pushMsgListAll() {
if (this.msgList.length && this.socket && this.socket.readyState === WebSocket.OPEN) {
// 发送头部数据
const msg = this.msgList.shift();
this.send(msg);
pushMsgListAll();
} else {
// 重连重新发送
this.connect();
}
}
// 消息发送
send(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
console.error('websocket 未连接或已断开,请查看!');
}
}
// 心跳检测
onHeartbeat() {
if (this.stopWebSocket) return;
if (this.heartbeatTimer) {
this.offHeartbeat();
}
this.heartbeatTimer = setInterval(() => {
if (this.socket) {
this.socket.send('ping,测试是否连接中');
} else {
console.error('websocket 未连接或已断开,请查看!');
}
}, 10000);
}
// 关闭心跳检测
offHeartbeat() {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
// 连接
connect() {
console.log(`WebSocket连接地址: ${this.url}`);
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
return;
}
this.socket = new WebSocket(this.url);
// !websocket连接成功
this.socket.onopen = event => {
this.stopWebSocket = false;
// 开启心跳检测
this.onHeartbeat();
console.log(`连接成功`);
// 判断是否有消息未发送成功
if (this.msgList.length) {
this.pushMsgListAll();
}
this.dispatchEvent('open', event);
};
this.socket.onmessage = event => {
this.dispatchEvent('message', event);
this.onHeartbeat();
};
this.socket.onclose = event => {
if (!this.stopWebSocket) {
// 断网重连逻辑
setTimeout(() => {
this.connect();
}, 5000);
}
this.dispatchEvent('close', event);
};
this.socket.onerror = event => {
this.offHeartbeat();
this.dispatchEvent('error', event);
};
}
// 关闭连接
close() {
if (this.socket) {
this.stopWebSocket = true;
this.socket.close();
this.socket = null;
this.removeEventListener('open');
this.removeEventListener('message');
this.removeEventListener('close');
this.removeEventListener('error');
}
this.offHeartbeat();
}
}










