今天我们来封装一下websocket的连接
首先我们新建一个js文件叫chatWs.js,下面是js的内容
// chatWs.js
import { Message } from "element-ui";
const STATE = {
HEART: "heartbeat",
LOGIN: "connection",
LOGOUT: "disconnection",
RECEIVED_MSG_RESULT: "receivedMsgResult", //收到消息给一个响应
MSG_RES: 'newMsg', // 新消息通知
}
// 导出一个类
export class chatWs {
constructor(){
this.jsocket = null; // 与 CTI 服务器,建立一个 websocket 对象
this.wsUrl = '';
this.reconnectTime = 6000; // 重新连接间隔时间值
this.keepaliveTime = 6000; // 保持连接间隔时间值
this.reconnectInterval = null; // 重新连接定时器
this.heartInterval = null; // 心跳定时器
this.headInterval = null; // 计算心跳是否还在的定时器
this.reconnected = false; // 是否是重连
this.isConnected = false; // 是否已经连接
this.callBack = {}; // 回调
this.domainConfig = {}; // webscoket的域名等配置项
this.sendMsgList = []; // 发送过的心跳消息集合
this.activeCloseWs = false; // 是否主动断开连接
this.userName = "";
this.msgSendResultTimer = {}; //消息发送是否成功定时器
this.msgSendResultTimmOutTime = 5000; // 消息发出之后未收到响应就认定发送失败的时间
}
init(config,cb){
if(!config.domainConfig){
let obj = {
code: 0,
message: '请先填写配置项'
}
this.errorMsgCallback(obj);
}
this.callBack = Object.assign({},cb);
this.domainConfig = config.domainConfig;
this.userName = config.userName;
if(!this.domainConfig.protocol)this.domainConfig.protocol = "ws";
this.wsUrl = this.domainConfig.isDomain ? `${this.domainConfig.protocol}://${config.domainConfig.domain}${config.domainConfig.suffix ? config.domainConfig.suffix : "" }` : `${config.domainConfig.protocol}://${this.domainConfig.domain}:${this.domainConfig.port}${config.domainConfig.suffix ? config.domainConfig.suffix : ""}`;
this.connectWs();
}
connectWs(){
if(this.jsocket)this.jsocket.close();
try{
this.jsocket = new WebSocket(this.wsUrl);
} catch (e){
this.jsocket = null;
this.errorMsgCallback({message: `连接${this.wsUrl}有误`});
return;
}
this.jsocket.onopen = (msg) => { // ws已经连接
this.wsOpen(msg);
}
this.jsocket.onmessage = (msg) => {
this.wsMessages(msg.data);
}
this.jsocket.onerror = (msg) => {
this.wsError(msg);
}
this.jsocket.onclose = (msg) => {
this.wsClose(msg);
}
}
wsOpen(msg){
this.isConnected = true;
this.activeCloseWs = false;
if(this.reconnectInterval)clearTimeout(this.reconnectInterval);
this.login();
}
wsMessages(msg){
try{
msg = JSON.parse(msg);
}catch(err) {
return;
}
if(msg.event !== STATE.HEART)this.receivedMsgResult(msg);
switch(msg.event){
case STATE.HEART:
const index = this.sendMsgList.findIndex(v => v.msgId === msg.msgId);
if(index > -1)this.sendMsgList.splice(index,1);
break
case STATE.LOGIN: // 登录成功发送心跳
this.sendHeart();
if(this.callBack.onLogin)this.callBack.onLogin(msg);
break
case STATE.LOGOUT: // 退出登录
if(this.callBack.onLogout)this.callBack.onLogout();
this.beforClose();
if(this.jsocket)this.jsocket.close();
break;
case STATE.MSG_RES: // 收到聊天消息
if(this.callBack.receviedMsg)this.callBack.receviedMsg(msg);
break;
default :
break
}
}
wsError(msg){
console.log('连接出错');
let obj = {
code : 0,
message: `连接${this.wsUrl}失败,请联系运维人员处理`
}
if(this.callBack.connectError)this.callBack.connectError(obj);
this.errorMsgCallback(obj);
this.beforClose();
if(!this.activeCloseWs)this.reConnectWs();//不是主动断开连接才发起重连
}
wsClose(msg){
console.log('ws close');
if(this.callBack.onLogout)this.callBack.onLogout();
this.beforClose();
}
reConnectWs(){
this.beforClose();
if(this.reconnectInterval)clearTimeout(this.reconnectInterval);
this.reconnected = true;
// this.connectWs();
this.reconnectInterval = setTimeout(()=> {
this.reconnected = true;
this.connectWs();
}, this.reconnectTime)
}
beforClose(){
if(this.heartInterval)clearInterval(this.heartInterval);
if(this.headInterval)clearInterval(this.headInterval);
this.isConnected = false;
}
closeWs(){
if(this.reconnectInterval)clearTimeout(this.reconnectInterval);
if(this.jsocket)this.jsocket.close();
this.activeCloseWs = true;
}
errorMsgCallback(err){
Message({
message: err.message ? err.message : '失败',
type: 'error'
});
}
sendMsg(msg){
if(!this.isConnected){
let obj = {
code: -1,
message: "ws未连接"
}
this.errorMsgCallback(obj);
return;
}
const msgId = this.uuid(32,16)
let publicObj = {
msgId: msgId,
timestamp: new Date().getTime(),
userName: this.userName
}
let obj = Object.assign(publicObj,msg);
if(obj.event && obj.event === STATE.HEART) this.sendMsgList.push(obj);
const str = JSON.stringify(obj);
this.jsocket.send(str);
}
// 收到消息之后给一个收到的回应
receivedMsgResult(row){
const obj = {
event : STATE.RECEIVED_MSG_RESULT,
msgId: row.msgId,
}
this.sendMsg(obj);
}
sendHeart(){
if(this.heartInterval)clearInterval(this.heartInterval);
if(this.headInterval)clearInterval(this.headInterval);
let obj = {
event: STATE.HEART,
}
this.sendMsg(obj);
//发送心跳包
this.heartInterval = setInterval(() => {
let obj = {
event: STATE.HEART,
}
this.sendMsg(obj);
}, this.keepaliveTime);
this.headInterval = setInterval(() => {
// 两次心跳没有响应间隔在三十秒之内
if(this.sendMsgList.length > 2
&& (this.sendMsgList[this.sendMsgList.length - 1].timestamp - this.sendMsgList[this.sendMsgList.length - 2].timestamp) / 1000 > 30
&& (this.sendMsgList[this.sendMsgList.length - 1].timestamp - this.sendMsgList[this.sendMsgList.length - 3].timestamp) / 1000 > 30 ){
let data = {
code: -11,
message: '超过30秒没有心跳,需要重新登录.'
}
this.errorMsgCallback(data);
this.beforClose();
this.jsocket.close();
setTimeout(()=>{
Message({
message: '将在6s之后进行重连',
type: 'info'
});
// 10秒之后进行重连
setTimeout(()=>{
this.reConnectWs()
}, 6000)
},4000)
}
}, 10000);
}
login(){
let obj = {
event : STATE.LOGIN,
isReConnection: this.reconnected ? 1 : 0,
token: sessionStorage.token,
type: 1,
}
this.sendMsg(obj);
}
uuid (len,radix) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
.split('');
var uuid = [], i;
radix = radix || chars.length;
if (len) {
// Compact form
for (i = 0; i < len; i++)
uuid[i] = chars[0 | Math.random() * radix];
} else {
// rfc4122, version 4 form
var r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
}
调用方式
// xxx.vue中
<script>
import {chatWs} from './chatWs.js';
export default {
data (){
this.chatWs = new chatWs();
return {
}
},
computed:{
callBack(){
return {
receviedMsg:(obj)=>{
// 收到消息的回调函数 do someing····
}
}
}
},
mounted(){
this.initWs();
},
beforeDestroy(){
console.log('page beforeDestroy');
this.chatWs.beforClose();
this.chatWs.closeWs();
},
methods:{
initWs(){
let config = {
domainConfig:{
domain: 127.0.0.1, // ip或者域名
port: 8866, // 端口
protocol: location.protocol === 'https:' ? 'wss' : 'ws',
suffix:'/api/websocket',// 可要可不要,具体看根据后端的ws地址来决定
isDomain: 0, // ws地址是否需要端口 0-需要(本地调试是ip地址需要端口) 1-不需要(线上域名不需要端口)
},
userName: sessionStorage.user ? JSON.parse(sessionStorage.user).email : '', // 连接ws的用户名
}
this.chatWs.init(config, this.callBack); // 发起ws连接
}
}
}
</script>