用node建立websocket通信实现聊天室

17 篇文章 0 订阅
16 篇文章 0 订阅

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)

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿泽不会飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值