嘘~ 正在从服务器偷取页面 . . .

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


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)

文章作者: 阿泽
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 阿泽 !
评论
  目录