服务端
WebSocketServer.js中
const webSocket = require("ws"); //引入ws服务器模块
const ws = new webSocket.Server({ port: 8000 }); //创建服务器,端口为8000
let clients = {};
let clientNum = 0;
ws.on("connection", (client) => {
//连接客户端
// console.log('client', client);
//给客户端编号,也就是参与聊天的用户
client.name = ++clientNum;
clients[client.name] = client;
// 用户的聊天信息
client.on("message", (msg) => {
console.log("用户" + client.name + "说:" + msg);
console.log(17, msg + ''); // 不转字符串就不会转码 ping变成<Buffer 70 69 6e 67>了
console.log(18, String(msg)); // 不转字符串就不会转码 ping变成<Buffer 70 69 6e 67>了
//广播数据发送输出 // 聊天室才需要启动广播给所有人 前后端沟通不需要广播
// broadcast(client, msg);
if ('ping' === msg + '') {
client.send("你跟我说ping我跟你说pong");
// console.log(21, '你跟我说ping我跟你说pong');
}
// msg = String(msg).substring(1, 3); // 前端传过来的字符串得到的时候是带了两个引号的,必须去除两个引号才能等于这里定义的字符串 先String()再正则剔除引号
// console.log(26, String(msg), "在吗", "在吗" === String(msg), msg + '', String("在吗"), "在吗".toString()); // msg是带引号的字符串,“在吗”是不带引号的
if ("在吗" === String(msg).substring(1, 3)) {
console.log('我在');
client.send("我在");
}
});
//报错信息
client.on("error", (err) => {
if (err) {
console.log(err);
}
});
// 下线
client.on("close", () => {
delete clients[client.name];
console.log("用户" + client.name + "下线了~~");
});
});
//广播方法
function broadcast(client, msg) {
for (var key in clients) {
clients[key].send("用户" + client.name + "说:" + msg);
}
}
启动服务端:node WebSocketServer.js
vue中:
在main.js同级建socket.js文件
var webSocket = null
var globalCallback = null //定义外部接收数据的回调函数
var pingInterval // 定义心跳
var pongInterval
var pingPong = 'ping'
var urlAll
//初始化websocket
function initWebSocket(url) {
urlAll = url
if ('WebSocket' in window) {
webSocket = new WebSocket(url) //创建socket对象
console.log(webSocket)
} else {
alert('该浏览器不支持websocket!')
}
clearInterval(pingInterval) // 移除心跳
clearInterval(pongInterval) // 移除心跳
// 心跳机制
pongInterval = setInterval(() => {
if (pingPong === 'ping') {
console.log('没有返回pong 重启webSocket')
initWebSocket(urlAll)
}
// 重置为ping 若下一次 ping 发送失败 或者pong返回失败(pingPong不会改成pong),将重启
console.log('上次心跳正常返回pong了')
pingPong = 'ping'
}, 9500)
pingInterval = setInterval(() => {
if (webSocket.readyState === 1) {
// 检查ws为链接状态 才可发送
pingPong = 'ping'
webSocket.send('ping') // 客户端发送ping 我发ping 服务端发pong
}
}, 5000)
wsEvent()
}
// 封装一个函数,处理websocket的几个回调函数
function wsEvent() {
//打开 // 用于连接成功之后的回调函数
webSocket.onopen = function () {
webSocketOpen()
}
//收信
webSocket.onmessage = function (e) {
webSocketOnMessage(e)
}
//关闭 // 用于指定连接关闭之后的回调函数
webSocket.onclose = function () {
// initWebSocket() //关闭重连
webSocketClose()
clearInterval(pingInterval) // 移除心跳
clearInterval(pongInterval) // 移除心跳
}
//连接发生错误的回调方法
webSocket.onerror = function () {
console.log('WebSocket连接发生错误')
// initWebSocket() //重连
}
}
//连接socket建立时触发
function webSocketOpen() {
// if (e === 'LOGIN') {
const data = {
type: 'CONNECT',
token: sessionStorage.getItem('token') || ''
}
sendSock(data, function () {})
// }
console.log('WebSocket连接成功')
}
//客户端接收服务端数据时触发,e为接受的数据对象
function webSocketOnMessage(e) {
console.log('接收到信息:', e.data)
if (e.data === '你跟我说ping我跟你说pong') {
// 心跳正常
console.log('心跳正常')
pingPong = 'pong'
}
// const data = JSON.parse(e.data) //根据自己的需要对接收到的数据进行格式化
const data = e.data //根据自己的需要对接收到的数据进行格式化
// console.log('globalCallback---data', data)
globalCallback(data) //将data传给在外定义的接收数据的函数,至关重要。
/*在此函数中还可以继续根据项目需求来写其他东西。 比如我的项目里需要根据接收的数据来判断用户登录是否失效了,此时需要关闭此连接,跳转到登录页去。*/
}
//发送数据
function webSocketSend(data) {
webSocket.send(JSON.stringify(data)) //在这里根据自己的需要转换数据格式
}
//关闭socket
function webSocketClose() {
//因为我建立了多个socket,所以我需要知道我关闭的是哪一个socket,就做了一些判断。
if (webSocket.readyState === 1 && webSocket.url === urlAll) {
webSocket.close() //这句话是关键,之前我忘了写,一直没有真正的关闭socket
console.log('对话连接已关闭')
}
}
//在其他需要socket地方调用的函数,用来发送数据及接受数据
function sendSock(agentData, callback) {
// console.log(106, callback)
globalCallback = callback //此callback为在其他地方调用时定义的接收socket数据的函数,此关重要。
//下面的判断主要是考虑到socket连接可能中断或者其他的因素,可以重新发送此条消息。
switch (webSocket.readyState) {
//CONNECTING:值为0,表示正在连接。
case webSocket.CONNECTING:
setTimeout(function () {
webSocketSend(agentData, callback)
}, 1000)
break
//OPEN:值为1,表示连接成功,可以通信了。
case webSocket.OPEN:
webSocketSend(agentData)
break
//CLOSING:值为2,表示连接正在关闭。
case webSocket.CLOSING:
setTimeout(function () {
webSocketSend(agentData, callback)
}, 1000)
break
//CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
case webSocket.CLOSED:
// do something
break
default:
// this never happens
break
}
}
//将初始化socket函数、发送(接收)数据的函数、关闭连接的函数export出去
export default {
initWebSocket,
webSocketClose,
sendSock
}
在main.js中引入它
import socketApi from './socket' //找到封装的socket.js文件
Vue.prototype.socketApi = socketApi //将其挂在原型上,这样 $socketApi就在所有的 Vue 实例中可用了。
在需要使用到webSocket的页面中启用它
data中:
wsUrl: 'ws://localhost:8000', //定义socket连接地址
wsType: 'CONNECT',
mounted() {} 或者created中:
//建立socket连接
this.socketApi.initWebSocket(this.wsUrl)
//data为和后端商量好的数据格式
const data = {
type: this.wsType,
msg: '说的话666'
}
this.websocketSend(data) // 这里发送信息给服务端
methods: {}中:
addplataccount() {
this.websocketSend('在吗')
},
// 接收socket回调函数返回数据的方法
getConfigResult(res) {
console.log('getConfigResult接收socket回调函数返回数据', res) //服务端返回的数据
// this.websocketSend('在吗') 这里res就会收到'我在'
},
websocketSend(data) {
//data为要发送的数据,this.getConfigResult为回调函数,用于在此页面接收socket返回的数据。
//至关重要!我一开始没写这个,就蒙了,咋才能到拿到回来的数据呢。
// this.socketApi.sendSock(data, 'sendSock-data')
this.socketApi.sendSock(data, this.getConfigResult)
},
beforeRouteLeave(to, from, next) {
//在离开此页面的时候主动关闭socket
this.socketApi.webSocketClose()
next()
},