1.简单的房间号聊天室
该聊天室为最基本的聊天室:
(1)浏览器端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="day1.css" type="text/css">
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.8/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-if="!isshow">
姓名:<input type="text" v-model="name"><br>
房间:<input type="text" v-model="roomid">
<button @click="enter">进入聊天室</button>
</div>
<div v-else>
<input type="text" v-model="msg">
<button @click="sendmessage">发送信息</button>
<button @click="closews">关闭ws</button>
<span>在线人数:{{num}}</span>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item}}
</li>
</ul>
</div>
</div>
<!-- <script src="day1.js"> -->
<script>
Vue.config.productionTip = false
var app=new Vue({
el:'#app',
data(){
return{
msg:'',
name:'',
isshow:false,
list:[],
ws:{},
num:0,
roomid:''
}
},
mounted() {
this.ws=new WebSocket('ws://127.0.0.1:3000');
this.ws.onopen=this.onOpen;
this.ws.onmessage=this.onMessage;
this.ws.onclose=this.onClose;
this.ws.onerror=this.onError;
},
methods: {
sendmessage(){
this.list.push("我:"+this.msg);
this.ws.send(JSON.stringify({
event:"message",
msg:this.msg,
}))
this.msg=''
},
onOpen(){
console.log("打开ws:"+this.ws.readyState);
},
onMessage(event){
//用户未登录接收不到消息
if(!this.isshow){
return
}
var thismsg=JSON.parse(event.data);
console.log(thismsg);
this.num=thismsg.personnumber
if(thismsg.name==this.name){
return
}else{
if(thismsg.event=='enter'){
this.list.push(thismsg.name+"进入聊天室");
}else if(thismsg.event=='out'){
this.list.push(thismsg.name+"退出聊天室");
}else{
this.list.push(thismsg.name+" :"+thismsg.msg);
}
}
},
onClose(){
console.log("关闭ws:"+this.ws.readyState);
},
onError(){
console.log("出错:"+this.ws.readyState);
},
closews(){
this.ws.close()
},
enter(){
if(this.name.trim()===""||this.roomid.trim()===""){
alert("空")
return
}
this.isshow=true;
this.ws.send(JSON.stringify({
event:"enter",
name:this.name,
roomid:this.roomid
}))
}
},
})
</script>
</body>
</html>
(2)服务器端
const WebSocket=require('ws');
const wss=new WebSocket.Server({port:3000});
let group={}
wss.on('connection',function connection(ws){
console.log("---------------------");
ws.on('message',function(msg){
const msgobj=JSON.parse(msg);
if(msgobj.event=='enter'){
ws.name=msgobj.name;
ws.roomid=msgobj.roomid;
if(typeof group[ws.roomid]==='undefined'){
group[ws.roomid]=1;
}else{
group[ws.roomid]++;
}
}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.personnumber=group[ws.roomid];
client.send(JSON.stringify(msgobj))
}
})
});
ws.on('close',function(msg){
if(ws.name){
group[ws.roomid]--;
}
msgobj={}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.personnumber=group[ws.roomid];
msgobj.event="out";
client.send(JSON.stringify(msgobj))
}
})
})
})
2.添加鉴权:
(1)浏览器端
<script>
Vue.config.productionTip = false
var app=new Vue({
el:'#app',
data(){
return{
msg:'',
name:'',
isshow:false,
list:[],
ws:{},
num:0,
roomid:''
}
},
mounted() {
this.ws=new WebSocket('ws://127.0.0.1:3000');
this.ws.onopen=this.onOpen;
this.ws.onmessage=this.onMessage;
this.ws.onclose=this.onClose;
this.ws.onerror=this.onError;
},
methods: {
sendmessage(){
this.list.push("我:"+this.msg);
this.ws.send(JSON.stringify({
event:"message",
msg:this.msg,
}))
this.msg=''
},
onOpen(){
console.log("打开ws:"+this.ws.readyState);
this.ws.send(JSON.stringify({
event:"auth",
message:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ind6eiIsImlhdCI6MTUxNjIzOTAyMn0.0WWry526SJ5FQ-9B7L_Y9dDBm16uh3o2LFR6ee8f_ew"//token数据
}))
},
onMessage(event){
//用户未登录接收不到消息
if(!this.isshow){
return
}
var thismsg=JSON.parse(event.data);
console.log(thismsg);
if(thismsg.event=='onauth'){
alert("请鉴权")
}
this.num=thismsg.personnumber
if(thismsg.name==this.name){
return
}else{
if(thismsg.event=='enter'){
this.list.push(thismsg.name+"进入聊天室");
}else if(thismsg.event=='out'){
this.list.push(thismsg.name+"退出聊天室");
}else{
this.list.push(thismsg.name+" :"+thismsg.msg);
}
}
},
onClose(){
console.log("关闭ws:"+this.ws.readyState);
},
onError(){
console.log("出错:"+this.ws.readyState);
},
closews(){
this.ws.close()
},
enter(){
if(this.name.trim()===""||this.roomid.trim()===""){
alert("空")
return
}
this.isshow=true;
this.ws.send(JSON.stringify({
event:"enter",
name:this.name,
roomid:this.roomid
}))
}
},
})
</script>
(2)服务器端
安装jsonwebtoken:
npm i jsonwebtoken
const WebSocket=require('ws');
const http=require('http');
const wss=new WebSocket.Server({noServer:true});
const server=http.createServer();
const jwt=require('jsonwebtoken');
const { log } = require('console');
let group={}
wss.on('connection',function connection(ws){
console.log("---------------------");
ws.on('message',function(msg){
const msgobj=JSON.parse(msg);
if(msgobj.event=='enter'){
ws.name=msgobj.name;
ws.roomid=msgobj.roomid;
if(typeof group[ws.roomid]==='undefined'){
group[ws.roomid]=1;
}else{
group[ws.roomid]++;
}
}
//鉴权:
if(msgobj.event==="auth"){
jwt.verify(msgobj.message,"wzz",(err,decode)=>{
if(err){
console.log("auth error");
return
}else{
console.log(decode);
ws.isAuth=true;
return
}
})
}
if(!ws.isAuth){
ws.send(JSON.stringify({
event:"onauth",
message:"please auth again"
}))
}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.personnumber=group[ws.roomid];
client.send(JSON.stringify(msgobj))
}
})
});
ws.on('close',function(msg){
if(ws.name){
group[ws.roomid]--;
}
msgobj={}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.personnumber=group[ws.roomid];
msgobj.event="out";
client.send(JSON.stringify(msgobj))
}
})
})
})
server.on('upgrade' , function upgrade( request,socket,head){
console.log("request : "+JSON.stringify(request.headers));
// authenticate(request, (err, client) => {
// if (err||!client) {
// socket.destroy();
// return;
// }
wss.handleUpgrade( request, socket, head,function done(ws){
wss.emit('connection',ws,request);
})
// })
})
server.listen(3000)
(3)node端
可直接传递token:
const WebSocket=require('ws');
const ws=new WebSocket('ws://127.0.0.1:3000',{
headers:{
token:'213'
}
});
3.心跳检查断线重连
(1)浏览器端
<script>
Vue.config.productionTip = false
var app=new Vue({
el:'#app',
data(){
return{
msg:'',
name:'',
isshow:false,
list:[],
ws:{},
num:0,
roomid:'',
handle:{}
}
},
// mounted() {
// this.ws=new WebSocket('ws://127.0.0.1:3000');
// this.ws.onopen=this.onOpen;
// this.ws.onmsg=this.onmsg;
// this.ws.onclose=this.onClose;
// this.ws.onerror=this.onError;
// },
methods: {
init() {
this.ws=new WebSocket('ws://127.0.0.1:3000');
this.ws.onopen=this.onOpen;
this.ws.onmessage=this. onMessage;
this.ws.onclose=this.onClose;
this.ws.onerror=this.onError;
},
sendmsg(){
this.list.push("我:"+this.msg);
this.ws.send(JSON.stringify({
event:"msg",
msg:this.msg,
}))
this.msg=''
},
onOpen(){
console.log("打开ws:"+this.ws.readyState);
this.ws.send(JSON.stringify({
event:"auth",
msg:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.33NBeLKafs6unecYcSJl7QUlfv3DfYvdf2TXSEPNYiY"//token数据
}))
this.ws.send(JSON.stringify({
event:"enter",
name:this.name,
roomid:this.roomid
}))
},
onMessage(event){
//用户未登录接收不到消息
if(!this.isshow){
return
}
var thismsg=JSON.parse(event.data);
console.log(thismsg);
switch(thismsg.event){
case 'onauth':
alert("请鉴权")
break;
case 'enter':
this.list.push(thismsg.name+"进入聊天室");
break;
case 'out':
this.list.push(thismsg.name+"退出聊天室");
break;
case 'heartbeat':
this.checkServer()
this.ws.send(JSON.stringify({
event:"heartbeat",
msg:"pong"
}))
break;
default:
if(thismsg.name!==this.name){
this.list.push(thismsg.name+" :"+thismsg.msg);
}
}
this.num=thismsg.pnum
},
onClose(){
console.log("关闭ws:"+this.ws.readyState);
},
onError(){
console.log("出错:"+this.ws.readyState);
//连接失败后一秒进行断线重连
var that=this
setTimeout(function(){
that.init()
},1000)
},
closews(){
this.ws.close()
},
enter(){
if(this.name.trim()===""||this.roomid.trim()===""){
alert("空")
return
}
this.isshow=true;
this.init()
},
checkServer(){
var that=this
clearTimeout(this.handle)
this.handle=setTimeout(function(){
that.onClose()
that.init()
},1000+500)
}
},
})
</script>
(2)服务器端
const WebSocket=require('ws');
const http=require('http');
const wss=new WebSocket.Server({noServer:true});
const server=http.createServer();
const jwt=require('jsonwebtoken');
const timeInterval=1000
let group={}
wss.on('connection',function connection(ws){
ws.isAlive=true;
ws.on('message',function(msg){
const msgobj=JSON.parse(msg);
if(msgobj.event=='enter'){
ws.name=msgobj.name;
ws.roomid=msgobj.roomid;
if(typeof group[ws.roomid]==='undefined'){
group[ws.roomid]=1;
}else{
group[ws.roomid]++;
}
}
//鉴权:
if(msgobj.event==="auth"){
jwt.verify(msgobj.msg,"wzz",(err,decode)=>{
if(err){
console.log("auth error");
ws.send(JSON.stringify({
event:"onauth",
msg:"please auth again"
}))
return
}else{
console.log(decode);
ws.isAuth=true;
return
}
})
return
}
if(!ws.isAuth){
return
}
//心跳监测:
if(msgobj.event=='heartbeat'&&msgobj.msg=="pong"){
ws.isAlive=true
return
}else{
// return
}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.pnum=group[ws.roomid];
client.send(JSON.stringify(msgobj))
}
})
});
ws.on('close',function(msg){
if(ws.name){
group[ws.roomid]--;
}
msgobj={}
wss.clients.forEach((client) => {
//判断非自己的客户端
if (client.readyState === WebSocket.OPEN&&client.roomid==ws.roomid){
msgobj.name=ws.name;
msgobj.pnum=group[ws.roomid];
msgobj.event="out";
client.send(JSON.stringify(msgobj))
}
})
})
})
server.on('upgrade' , function upgrade( request,socket,head){
wss.handleUpgrade( request, socket, head,function done(ws){
wss.emit('connection',ws,request);
})
})
setInterval(() => {
wss.clients.forEach((ws) => {
if(!ws.isAlive&&ws.roomid){
group[ws.roomid]--;
delete ws['roomid']
return ws.terminate()
}
ws.isAlive=false;
ws.send(JSON.stringify({
event:'heartbeat',
pnum:group[ws.roomid],
msg:'ping',
}))
})
},timeInterval);
server.listen(3000)