MDN - WebSocket
编写 WebSocket 服务器
// Web Scoket 封装类方法export default class WebSocketClass {constructor(options) {// websocket对象this.ws = null;// websocket是否关闭this.socketStatus = null;// 消息队列,未成功发送的队列this.socketMsgQueue = [];// 手动记录socket状态 -1:未连接, 0:连接中, 1:已连接 (由于socket存在假死状态不推荐使用readyState)this.connectStatus = -1;// 重连定时器this.reConnInterval = null;// 重连次数this.RECONNECTION_NUMBER = 20;// 记录重连次数this.reconnectionCount = 0;// 是否开启定心跳检测this.isHeartCheck = false;// 心跳检测 定时器 初始化this.heartTimeout = null;this.heartServerTimeout = null;// 链接websocketthis.initConnectSocket();// 访客进入this.visitor = options.visitor;// 消息推送this.pushMsg = options.pushMsg;// 删除消息this.removeChatList = options.removeChatList;// 连接/重连 拉取最新消息(可socket返回或ajax请求拉取数据)this.reConnectGetMsg = options.reConnectGetMsg;}// 获取socket链接地址getWSURL() {return "ws://121.40.165.18:8800";}// 初始化 连接websocketinitConnectSocket() {if (!Reflect.has(window, "WebSocket")) {return console.log("您的浏览器不支持WebSocket");}// 清除重连定时器this.reConnInterval && clearInterval(this.reConnInterval);// 判断socket状态if (this.ws && this.ws.readyState === 1) {this.ws.close();this.wss = null;}this.connectStatus = 0;this.ws = new WebSocket(this.getWsURL());// WebSocket 连接成功后的回调函数this.ws.addEventListener("open",() => {console.log("websocket connection successful");this.connectStatus = 1;// 连接次数归零this.reconnectionCount = 0;//处理未发送或者发送失败消息this.socketMsgQueue.forEach((item) => {// 重新发送失败的消息this.socketSendMessage(item);});this.socketMsgQueue = [];this.reConnectGetMsg();// 判断是否启动 socket 心跳检测if (this.isHeartCheck) {this.heartCheck();}},false);// 监听服务器接受到信息时的回调函数this.ws.addEventListener("message",(res) => {let message = {};if (res.data && typeof res.data !== "object") {message = JSON.parse(res.data);}// 批量处理if (Array.isArray(message)) {message.forEach((item) => {this.processMessage(item);});} else {this.processMessage(message);}},false);// WebSocket 连接关闭后的回调函数this.ws.addEventListener("close",(error) => {console.log(`socket has been closed, ${error}`);this.connectStatus = -1;this.reLinkSocket();},false);// WebSocket 连接失败后的回调函数this.ws.addEventListener("error",(error) => {console.log(`socket has been error, ${error}`);this.connectStatus = -1;this.reLinkSocket();},false);}// 监听信息推送processMessage(message) {// 接受到心跳检测返回值if (message === "pong") {return this.heartCheck();}// 访客进入if (message.types === "访客进入") {this.visitor(message);}console.log(message);}// 发送信息async sendMessage(data) {console.log(`发送消息给服务器:${data}`);// 发送消息时,先添加到消息列表显示出来// TODO:不能等服务器返回在显示,由于消息服务器可能存在积压状态导致消息推送过慢情况处理this.pushMsg(premsg);if (this.connectStatus === 1) {return this.ws.send(data);}this.socketMsgQueue.push(message);await this.closeSocket();await this.reLinkSocket();}// 撤回消息withdrawMessage(data) {this.sendMessage(data);// 删除消息this.removeChatList(data);}/*** @method 接收消息后格式化消息* @param {Object} message 消息内容* @param {Boolean} isOwn 是否属于自己发出的消息* @return {Array} 返回格式化后消息*/formatMessageTalk(message, isOwn) {// some code...}// 心跳包检测heartCheck() {// 心跳时间与收发内容以后端约定为主this.heartTimeout && clearTimeout(this.heartTimeout);this.heartServerTimeout && clearTimeout(this.heartServerTimeout);this.heartTimeout = setTimeout(() => {// 检查socket链接状态 才可发送if (this.connectStatus === 1) {this.ws.send("ping");}// 如果5s内没有返回约定值判定socket处于假死状态,重新链接socketthis.heartServerTimeout = setTimeout(async () => {console.log("heartCheck timeout");await this.closeSocket();await this.reLinkSocket();}, 5000);}, 20000);}// 关闭WebSocketcloseSocket() {if (this.ws) {this.ws.close();this.ws = null;// 清除重连定时器this.reConnInterval && clearInterval(this.reConnInterval);// 清除心跳包定时器this.heartTimeout && clearTimeout(this.heartTimeout);this.heartServerTimeout && clearTimeout(this.heartServerTimeout);}}// 断线重连// TODO: 严谨写法需要判断主动重连与被动重连情况reLinkSocket() {console.log("reconnection webscoket function");// 清除心跳包定时器this.heartTimeout && clearTimeout(this.heartTimeout);this.heartServerTimeout && clearTimeout(this.heartServerTimeout);// 清除重连定时器this.reConnInterval && clearInterval(this.reConnInterval);this.reConnInterval = setInterval(() => {// 判断是否达到重连次数if (this.reconnectionCount < RECONNECTION_NUMBER) {this.connectSocket();this.reconnectionCount += 1; // 重连次数} else {clearInterval(this.reConnInterval);}}, 5000);}}// use caseconst websocket = new WebSocketClass({// 访客进入visitor: (userInfo) => {console.log(userInfo);// some code...},// 消息推送pushMsg: (msgdata) => {console.log(msgdata);// some code...},// 删除消息removeChatList: (messageId) => {console.log(messageId);// some code...},// 连接/重连 拉取最新数据reConnectGetMsg: () => {// 拉取最新消息},});
