node+express(socket.io)+vue(vue-socket.io)实现私密聊天室

使用的技术有

  1. express
  2. socket.io
  3. vuex
  4. vue-router
  5. vue-socket.io
  6. MUI
  7. mint-ui

连接服务端

Vue.use(new VueSocketIO({  //进入当前页面时 注册socket
   // debug: true, //开启调试
  connection: 'ws://127.0.0.1:3001',  //服务端地址
  vuex: {  //vuex存储
    store,
    actionPrefix: 'SOCKET_',
    /*
       在vuex的实例的 action 里面  这样
       就会自动收到  服务端返回的数据

       下面 就是在 mutation 里面同样配置
     SOCKET_message:(state, data)=>{
        console.log(data);
    }
     */
    mutationPrefix: 'SOCKET_',
    options:{
 
 		//使用命名空间
      useConnectionNamespace: true
    }
  }
}));

登录逻辑

 sockets:{
            connect(){
                // console.log("正在登录");
            },
            login(data){

                //判断其是否登录成功
               if (data.code === 0){
                   alert("该用户已存在")
               } else {
                   this.$router.push('/index/xiaoxi');
               }
            }
        },
        methods:{
            register(){
                this.$router.push('/register')
            },
            login(){
                this.$socket.emit('login',{username:this.username});
       }

初始化好友列表

 sockets: {
            connect(){

            },
            //初始化所有在线的朋友
            allFriend(data){

                // 获取 除自己外所有 成员
              this.friendList = data.users.filter((user,index)=>{
                  return this.username !== user.username;
              });
            }
        },
        created() {

            //获取自己的名字
            if (this.$store.state.user) {
                this.username = this.$store.state.user;
            } else {
                this.$router.push('/login');
            }

            //初始化 好友列表
            this.$socket.emit('allFriend');
        },
        methods: {
            gotoChatRoom(username) {

                //跳转时 带上 要发私信的用户名
                this.$router.push('/chatRoom?tousername='+username);
            }
        }

聊天逻辑

    export default {
        data() {
            return {
                text: '',
                username: "",
                toUsername:'',
                fromRouterPath: '',
                btnDisabled: true,
                socketID:'',
                allChatInfo:{}
            }
        },
        sockets: {
            /**
             * 此处的相关操作和  QQ.html  里面差不多
             */
            connect(){  //监听连接是否成功
                //连接成功 获取当前连接的ID
                // this.socketID =this.$socket.id;
            },
            message(data){  //接收 服务器传来的数据
                this.allChatInfo = data;
            },
            messageHistory(data){  //历史记录
                this.allChatInfo = data;
            }
        },
        beforeRouteEnter(to, from, next) {
            next(vm => {
                //获取上一次的路由//返回上一页
                vm.fromRouterPath = from.fullPath;
            })
        },
        created() {
            //获取当前的用户名
            this.username = this.$store.state.user;

            //1,获取用户名(要发私信给他的那个)
            this.toUsername = this.$route.query.tousername;

            //初始化历史聊天记录
            this.$socket.emit('messageHistory',{username:this.username});


            // console.log(this.$socket);  //当前socket对象
        },
        methods: {
            sendClick() {

                //3,发送给服务器(数据:发送的数据,to用户名,自己的用户名)
                this.$socket.emit('send',{msg:this.text,toUser:this.toUsername,myUsername:this.username});


                //发送给服务器   并且带上自己的socketID
                // 注意 :这是默认的连接  也就是  '/'  ------下面使用namespace
                // this.$socket.emit('send',{data:this.text,socketID:this.socketID});


                /*
                namespace  发送一样
                 */
                // this.$socket.emit('namespaceSend',{data:this.text,socketID:this.socketID});


                /*
                房间
                 */
                // this.$socket.emit('send',{data:this.text,socketID:this.socketID});


                //动态接收服务器给的值???
                // this.sockets.subscribe('message', (data) => {
                //     console.log(data);

                //销毁  动态添加的事件
                //     this.sockets.unsubscribe('message');
                // });


                //清空
                this.text = '';
                this.btnDisabled = true;
            },
            gotoBack() {
                //返回上一页面
                this.$router.push(this.fromRouterPath);
            },

            //判断是否有输入
            isInput() {
                if (this.text.trim().length > 0) {
                    this.btnDisabled = false;
                } else {
                    this.btnDisabled = true;
                }
            }
        },
        filters:{
        }
    }

服务器端

"use strict";

const app = require('express')(); //创建express实例  express作为中间件
const server = require('http').createServer(app); //创建http服务器
const io = require("socket.io")(server); //将创建的http服务添加到socket.io
const BodyParser = require('body-parser');
const cors = require('cors');


//配置post请求
// app.use(BodyParser.urlencoded({extended:false}));
// app.use(BodyParser.json());

//跨域
// app.use(cors({
//     origin:["http://127.0.0.1:8080","http://192.168.43.122:8080",'http://localhost:8080'],
//     methods:["POST","GET"],
//     alloweHeaders:['Conten-type','Authorization']
// }));

let personNum = 0;
let users = [];   //存储 用户信息(用户名,发送的信息,图片URL,avatarUrl)
let _sockets = {}; //以用户名 为键 存储socket连接(对应的映射)
io.on('connection',(socket)=>{

    /**
     * 实现私密聊天********************************************************
     */
    socket.on('login',data=>{   //登录
        socket.username = data.username;
        if (data.username){
            for (let user of users){
                if (user.username === data.username){
                    socket.emit("login",{msg:'已经存在',code:0});
                    socket.username = null;
                    //如果已经存在 置为空 //下面判断  说明是旧用户,需要换一个用户名
                    break;
                }
            }
        }

        //不存在  保存起来 (新用户登录成功)
        if (socket.username){
            users.push({
                username: data.username, //用户名
                message: [],  //消息
                imgUrl: [],  //发送的文件
                styleSign:'', //个性签名
                avatarUrl: 'https://img02.sogoucdn.com/net/a/04/link?appid=100520040&url=https://i04piccdn.sogoucdn.com/6a8cddcfaa27dddc'
            });

            // 告知其 自己 登录成功(前端使用vuex保存数据)
            socket.emit('login',{msg:'登录成功', code:1, username:socket.username});

            //登录成功广播 告知所有用户 上线了
            io.emit('allFriend',{code:1,users})
        }

        //保存 socket (注意由于每次都是新的,所以需要更新或者保存)
        _sockets[data.username] = socket;


        //客户端朋友列表的初始化
        socket.on('allFriend',()=>{
           socket.emit('allFriend',{code:1,users});
        });


        //客户端历史消息初始化
        socket.on('messageHistory',data=>{
            for(let user of users){
                if (data.username === user.username){
                    _sockets[data.username].emit('messageHistory',user);
                    break;
                }
            }
        });


        //接收发送的数据并为其做出反应
        socket.on('send',data=>{
            if (data.msg){

                //先把数据存储起来
                users.forEach((user,index)=>{
                    //在发送方保存一份(username是自己的名字),
                    // 在接收方保存一份(username都是对方的名字)
                    if (user.username === data.myUsername || user.username === data.toUser){
                        user.message.push({
                            username:data.myUsername,  //用户名 是谁发的
                            userAndToUser:`${data.myUsername}${data.toUser}`,  //和谁一起聊天的数据
                            message: data.msg  //数据
                        })
                    }

                    //给自己一份数据
                    for (let user of users){
                        if (user.username === data.myUsername){
                            _sockets[data.myUsername].emit('message',user);
                            break;
                        }
                    }

                });

                //发送给指定的用户(返回他自己的数据即:to谁就把给他自己),如上还有发给自己一份
                for(let user of users){
                    if (user.username === data.toUser){  //找到要发给他数据的那个人
                        _sockets[data.toUser].emit('message',user);  //把她自己的数据发给她自己
                        break;
                    }
                }

            }
        })


        // console.log(users);
        // console.log(_sockets);
    });


    /***********************************************************************
    每位用户 连接后  都会有一个 属于他的 socket
    也就是 socket 是一个对象 和
    前端 this.$socket;  是同一个对象

    参考文档:
        https://www.cnblogs.com/edwardloveyou/p/10625152.html
        https://socket.io/docs/    //官方文档
     */

    let allUserSocket = [];
    // console.log(socket);
    /**
     * 特别注意:这里绑定的msg 一定要与
     * 前端 socket.on 接收数据绑定的 事件一样
     */
    // socket.emit('message', { "name":'zt' });// 向客户端发送消息

    // io.emit('msg',"所有客户端都能接收");//向所有客户端发送消息

   // socket.on('send',function (data) {   //接收客户端传来的
        // broadcast 默认是向所有的socket连接进行广播,
        // 但是不包括发送者自身,如果自己也打算接收消息的话,使用:io.emit。
   //     socket.broadcast.emit('message',data)

  //

    /***************************************************************************
     * rooms  房间  暂时不会
     */
       // socket.join('room');
       // socket.on('send',function (data) {
            // socket.broadcast.emit('message',data)
            // 向房间里的所有客户端发送消息
            // io.to('room').emit("message",data);
      //  });


    // 默认房间(每一个id一个room)
    // socket.on('send', (id, msg) => {
    //     console.log(id, msg);
    //     socket.broadcast.to(id).emit('message', msg);
    // });




   // socket.on('reply', () => {});


    /*
    断开连接
     */
    socket.on('disconnect',()=>{

        //删除退出聊天的人
        //触发用户离开的监听
        // socket.broadcast.emit("oneLeave",{username: socket.username});

        users.forEach((user,index)=> {
            if (user.username === socket.username) {
                users.splice(index, 1);
            }
        });
        console.log(socket.username + "先闪了");
        // console.log(socket);
    });
});

/************************************************************************************
 * namespace 命名空间    某个功能模块 单独的连接

  前端连接时  这样 [ 服务器IP/自己命名空间 ]
 */
const mypath = io.of('/mynamespace').on('connection',socket=>{
    socket.on('namespaceSend',data=>{
        console.log(data);
    })
});


/***********************************************************************************
 * 中间件   暂时忽略
 */
// const auth = (socket,next)=>{
//     const data = socket.request;
//     // if(!verify(data)){
//     //     throw new Error('not verify');
//     // }
//     next();
// };
// mypath.use(auth);

server.listen(3001,function () {
    console.log("runing");
});

你可能感兴趣的:(前端)