因为h5中提供了websocket模块, 所以我们可以直接使用, 不需要使用其他三方库, 为了方便管理websocket的各种状态, 及消息的发送接受, 我们可以单独创建一个js文件来管理.
vue前段代码
创建websocketManager.js文件
// 定义websocket 地址
let socketurl = "ws://192.168.0.252:8091";
// 重连锁, 防止过多重连
let reconnectLock = false;
// 定义一个消息发送中(包含发送失败的)的字典
window.messageSendingDic = {};
// 定义一个消息websocket连接状态的字段, 并且要绑定到widow上, 方便调用
// 0 未连接, 1 连接成功 2 连接中
window.webSocketState = 0;
// 定义连接服务器方法
function connectWebsocket(){
//如果用户已登录, 进行连接websoket, 如果没有登陆, 登录后进行连接 用token判断
// 创建一个websocket连接
// let webSocket = new WebSocket(socketurl);
// 如果想要传token, 因为ws不支持通过设置header, 所以直接在地址中加参数,
// 如ws://192.168.0.252:8091?name=lulu&token=123456
let name = "lulu";
let token = "1234"
let webSocket = new WebSocket(socketurl+`?appname=${name}&token=${token}`);
// 监听webSocket的各个状态
// 连接成功
webSocket.onopen = function() {
console.log("websocket连接成功")
// 连接成功后将连接状态改变
window.webSocketState = 1;
// 连接成功后, 要将消息队列里面的消息重新发送出去(底层重发, 和页面无关)
for(let session in window.messageSendingDic){
session.forEach(message => {
// 重发消息
reSendMessage(message)
});
}
}
// 连接出错
webSocket.onerror = function(error){
console.log("websocket连接出错", error);
// 进行重连
reconnectWebsocket();
}
// 连接关闭
webSocket.onclose = function(result){
console.log("websocket连接关闭", result);
if(result == "退出登录"){
return
}
// 进行重连
reconnectWebsocket();
}
// 接受到消息
webSocket.onmessage = function(message){
console.log("websocket接受到消息", message);
// 将受到的消息进行分类, 分发处理
formatAcceptMessage(message)
}
// 将webSocket绑定到window上面, 方便后续调用
window.webSocket = webSocket;
}
// 定义重连方法 如果连接失败, 或者关闭,
function reconnectWebsocket(){
// 如果正在重连, 则返回
if(reconnectLock){
return;
}
// 进行加锁
reconnectLock = true;
// 重连时将连接状态改变
window.webSocketState = 2;
// 为了防止过多请求, 1s后进行重连
setTimeout(function(){
// 解锁
reconnectLock = false;
// 进行连接, 如果失败接着重连
// connectWebsocket();
}, 1000)
}
/**
* 关闭websocket 退出时会用到
*
*/
function closeWebsocket(){
window.webSocket.onclose("退出登录")
}
// 定义发送消息的方法 message 格式为json
/**
*
* @param {
* message: "内容",
* id: "xxxxxxx"
* } message 消息内容
* @param "1" messageType 消息类型
* @param "QueryMsg" messageClass 附加字段吗消息类, 这里是以protobufjs的消息类为例
*/
function sendMessage(message, messageType, messageClass) {
// 这里可以对message做一些格式化处理
let formaterMessge = message;
// 将消息添加到发送中的数组中进行记录
// 先判断该回话有没有对应的数组, 如果没有就创建, 在添加, 如果有直接添加
if(window.messageSendingDic[message.sessionId]) {
window.messageSendingDic[message.sessionId].push(formaterMessge);
} else {
window.messageSendingDic[message.sessionId] = [];
window.messageSendingDic[message.sessionId].push(formaterMessge);
}
// 如果websocket连接成功, 进行发送消息
if(window.webSocketState == 1) {
formaterMessge = JSON.stringify(formaterMessge)
// 这里就可以直接用window调用了
window.webSocket.send(formaterMessge);
} else {
// 如果websocket没有连接成功, 直接告诉消息发送页面消息发送失败, 模拟接受到消息, 发给对应页面
let formaterMessge = {};
// 将处理后的消息进行发送通知, 通知给需要的页面进行处理, 在需要的页面进行监听
// 注意: 使用页面添加window.addEventListener("acceptNewMessage", this.testAction)
window.dispatchEvent(new CustomEvent("acceptNewMessage", formaterMessge));
}
}
// 定义重发送消息的方法 message 格式为json
/**
*
* @param {
* message: "内容",
* id: "xxxxxxx"
* } message 消息内容
* @param "1" messageType 消息类型
* @param "QueryMsg" messageClass 附加字段吗消息类, 这里是以protobufjs的消息类为例
*/
function reSendMessage(message) {
// 如果websocket连接成功, 进行发送消息
if(window.webSocketState == 1) {
// 这里就可以直接用window调用了
window.webSocket.send(message);
}
}
// 定义收到消息进行消息解析的方法
function formatAcceptMessage(message) {
// 处理消息. 格式化
let formaterMessge = message;
// 将发送成功的消息从发送中移除
if(window.messageSendingDic[message.sessionId]) {
let sendingArray = window.messageSendingDic[message.sessionId];
// 过滤发送成功的
window.messageSendingDic[message.sessionId] = sendingArray.filter(msg => {
return msg.id != message.id
});
}
// 将处理后的消息进行发送通知, 通知给需要的页面进行处理, 在需要的页面进行监听
// 注意: 使用页面添加window.addEventListener("acceptNewMessage", this.testAction)
window.dispatchEvent(new CustomEvent("acceptNewMessage", formaterMessge));
}
// 如果服务器端有消息确认, 可以根据消息确认, 添加消息是否发送成功的状态,
// 需要单独创建一个数组, 用来存放发送中的数据(包含发送失败的数据)
module.exports = {
connectWebsocket,
sendMessage
}
在main.js中引入
import {connectWebsocket} from "@/manager/webSocketManager";
//如果用户已登录, 进行连接websoket, 如果没有登陆, 登录后进行连接
if(token){
connectWebsocket();
}
会话页面chat.vue
import {sendMessage} from "@/manager/webSocketManager"
clickSendButton(){
console.log("点击了发送消息")
sendMessage({
sessionId: "1234567890",
userId: this.name,
messageId: "100000",
message: {
type: "text",
content: this.message
},
})
},
注意: websocket默认如果长时间发消息, 会断开(服务器设置websocket永远不断开除外), 所以需要设置心跳包进行websocket保活.
退出账号是要关闭socket , 主动退出要处理重连问题
// 退出登录
logout (ctx) {
// 主动关闭连接
websocket.onclose("退出登录")
Message.success('退出成功,请重新登录')
}
node 后台代码
node 不能直接使用websocket,需要安装支持websocket的包
这里使用的三方库nodejs-websocket 或ws
或者socket.io
安装
npm install nodejs-websocket
或
npm I ws
或者
npm install socket.io
在主页面加入以下代码
const ws = require("nodejs-websocket");
//定义一个对象,用于存放正在连接中的socket, 字段名是以token命名
const conningObjet = {};
let webServe = ws.createServer(function (connection) {
// console.log('创建成功', connection)
//连接成功的回调
// 获取连接的token
let path = connection.path;
let pathParams = getParamsFromURL(path);
console.log(pathParams);
// 不满足服务器条件时服务器主动断开连接
if(pathParams.token) {
// 如果token正确进行继续操作
//如果是第一次连接, 添加到对应的数组, 如果不是, 不用继续添加
if (!conningObjet[pathParams.token]) {
//将用户发来的信息对所有用户发一遍
conningObjet[pathParams.token] = connection;
}
//监听数据,当客户端传来数据时的操作
connection.on("text", function (result) {
console.log('接受到消息', result)
// console.log(result)
console.log(conningObjet.keys)
//数据只能是以字符串或buffer形式传递,所以这里要转换成JSON;
var data = JSON.parse(result);
// 收到消息后立即向服务器端发送确认消息
// 确认消息添加确认消息类型, 1 表示确认消息
data.type = "1";
connection.sendText(JSON.stringify(data))
})
connection.on('connect', function(code) {
console.log('开启连接', code)
})
connection.on('close', function(code, reason) {
console.log('关闭连接', code)
console.log('关闭原因:', reason)
console.log(conningObjet);
// 获取连接的token
let path = connection.path;
let pathParams = getParamsFromURL(path);
console.log(pathParams.token);
// 连接关闭时要将这个连接从连接对象中移除
delete conningObjet[pathParams.token]
console.log(conningObjet);
})
connection.on('error', function(code) {
console.log('异常关闭', code)
})
} else {
// 如果token 不合理就进行断开
connection.close(1, "token不正确");
}
});
webServe.listen(8091);
webServe.on('connection', connection => {
console.log("游客户端进行连接");
})
// 获取url上的参数, 使用的是正则表达式
function getParamsFromURL(url) {
const regex = /[?&]([^=#]+)=([^&#]*)/g;
const params = {};
let match;
while (match = regex.exec(url)) {
params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
}
return params;
}