唯品秀前端博客
当前位置: 前端开发 > Node+Koa > 基于vue+koa2+socket.io实现多人在线实时聊天

基于vue+koa2+socket.io实现多人在线实时聊天

2019-09-04 分类:Node+Koa 作者:管理员 阅读(879)

在群里看到好几次有关于websocket通信问题,作为一个前端,为嘛要懂的那么多,说好的一起做好切图仔呢,还能不能愉快地玩耍,哎!真叫人捉急。于是乎也去研究一番多人群组聊天室,发现还是有点意思,有那么点成就感的。

任务目标

实现多人在线实时聊天,即,类似于QQ群聊聊天室功能,支持用户上线下线通知(demo暂未实现,觉得没意思了,通信已经完全打通,剩下的都是业务层问题)、当前在线实时人数等。技术站:前端vue.js+vue-socket.io,服务端:node.js+koa2+mongodb+socket.io

socket.io是什么?

是对websocket的封装。并不是所有的客户端都可以支持websocket。如果支持websocket,那么socket.io就等同于websocket。socket.io还用到了其它的技术来模拟websocket,所以当你是用socket.io的时候,不管浏览器是否支持websocket,你都可以实现异步操作。当然如果你的客户端是用了socket.io,server端也必须对应使用。

服务端

main.js入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const Koa = require('koa');
const app = new Koa();//实例化
const Router = require('koa-router'); //注意:引入的方式
const bodyParser = require('koa-bodyparser');//处理post请求
const cors = require('koa2-cors');// CORS是一个W3C标准,全称是"跨域资源共享"
const { connect, initSchemas } = require("./mongodb");
const mongoose = require('mongoose');

//核心,初始化app作为HTTP 服务器的回调函数像express这么写mac电脑会有问题,无法访问
// const server = require('http').Server(app);  //express写法
const server = require('http').Server(app.callback());  //koa正确姿势

const io = require('socket.io')(server); //核心,实现客户端之间交互通信

<!-----------------------省略一小坨代码,这不是本次demo重点------------------------->

let sum = 0;
io.on('connection', socket => {
    console.log('初始化成功!');
    const Message = mongoose.model('Message')
    Message.find({}, function (err, res) {
        io.emit('getMsg', res);
    })
    //新人进来在线人数+1
    socket.on('users',data=>{
        sum = sum + 1;
        io.emit('users',sum); //将消息发送给所有人。
    })
    //disconnnect断开,自带函数方法
    socket.on('disconnect',data=>{
        console.log('用户断开了');
        sum = sum - 1;
        io.emit('users',sum); //将消息发送给所有人。
    })
    socket.on('send', data => {
        // console.log('客户端发送的内容:',data, data['name'], data['getMsg']);
        try {
            const Message = mongoose.model('Message')
            let oneUser = new Message({ name: data['name'], msg: data['getMsg'] })
            oneUser.save().then(() => {
                const Message = mongoose.model('Message')
                let dataArry = Message.find({}, function (err, res) {
                    console.log('获取到数据', res)
                    socket.emit('getMsg', res); //通知触发该方法的客户端
                    io.emit('getMsg', res); //通知所有客户端
                })
            })
        } catch (error) {
            console.log("失败",error)
        }
    })
})

server.listen(process.env.PORT || port, () => {
    console.log(`监听地址: http://127.0.0.1:${port}`);
})

前端代码

main.js入口文件

1
2
3
4
5
6
import VueSocketIO from 'vue-socket.io'  //核心

Vue.use(new VueSocketIO({
  debug: true, //设置true可以让你的控制台文字变颜色,更好的调试阅读
  connection: 'http://172.18.30.90:3000',  
}))

组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<template>
  <div class="content">
    <!-- <h1>{{ msg }}</h1> -->
    <h1>多人实时在线聊天</h1>
    <p>当前在线人数:{{userNmber}}</p>
    <input type="请输入内容" v-model="inValue" @keyup.enter="btn_sbmit">
    <input type="button" value="发送" @click="btn_sbmit">
    <ul>
      <!-- 最新加入 -->
      <li v-if="newUser">有新人加入</li>
      <li v-for="(item,index) in speak" :key="index" :class="{active:item.name == userStorage}">
        <span>
          <font v-if="item.name == userStorage"></font>
          {{item.name}}
          <font v-if="item.name != userStorage"></font>
        </span>
        {{item.msg}}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      speak:[], //聊天记录
      inValue:'', //当前输入信息
      userIp:'未知用户',
      userStorage: localStorage.getItem("userId"),
      newUser:false, //是否有新人加入
      userNmber: 0 //在线人数
    }
  },
  created () {
      fetch('http://172.18.30.90:3000/home/b')
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      //console.log(myJson);
    });
  },
  mounted () {
    this.$socket.emit('connect', 1);
    console.log(this.userStorage)
  },
  methods: {
    //提交向后端发送数据
    btn_sbmit() {
      if(!localStorage.getItem("userId")){
        this.userIp = this.formatDateTime() + Math.random().toString(36).substr(2);
        this.speak.push({'name':this.userIp.substr(-5, 5),msg:this.inValue})
        this.$socket.emit("send",{name:'用户'+this.userIp.substr(-5, 5), getMsg:this.inValue})
        localStorage.setItem("userId",'用户'+this.userIp.substr(-5, 5));
        this.userStorage = localStorage.getItem("userId");
      }else{
        this.speak.push({'name':localStorage.getItem("userId"),msg:this.inValue})
        this.$socket.emit("send",{name:localStorage.getItem("userId"), getMsg:this.inValue})
      }
      this.inValue = ''
    },
    //生成id
    formatDateTime(){
      var date = new Date();
          var y = date.getFullYear();
          var m = date.getMonth() + 1;
          m = m < 10 ? ('0' + m) : m;
          var d = date.getDate();
          d = d < 10 ? ('0' + d) : d;
          var h = date.getHours();
          var minute = date.getMinutes();
          var second = date.getSeconds();
          return y + m + d + h + minute + second;
      }
  },
  sockets:{
    connect(data){
      if(data){
        console.log('连接成功',data)
        this.$socket.emit("users")
      }
    },
    users(data){
      console.log("在线人数",data)
      this.userNmber = data;
    },
    reconnect(data){
      console.log('重新连接',data)
    },
    disconnecting(data){
      console.log('socket已断开连接');
      this.$socket.emit("users")
    },
    //有新人加入
    // userinfoNumber(data){
    //   this.userNmber = true;
    // },
    getMsg(data){
      console.log("后端传过来的消息",data)
      this.speak = data
    }
  }
}
</script>

注意:这段代码是和你的methods、mounted等生命周期同级的

最终效果

附:socket.io自带的事件

Socket.IO内置了一些默认事件,我们在设计事件的时候应该避开默认的事件名称,并灵活运用这些默认事件钩子是很有必要的。

服务器端事件:

1
2
3
4
io.sockets.on(‘connection’, function(socket) {}):socket连接成功之后触发,用于初始化
socket.on(‘message’, function(message, callback) {}):客户端通过socket.send来传送消息时触发此事件,message为传输的消息,callback是收到消息后要执行的回调
socket.on(‘anything’, function(data) {}):收到任何事件时触发
socket.on(‘disconnect’, function() {}):socket失去连接时触发(包括关闭浏览器,主动断开,掉线等任何断开连接的情况)

客户端事件:

1
2
3
4
5
6
7
8
9
10
connect:连接成功
connecting:正在连接
disconnect:断开连接
connect_failed:连接失败
error:错误发生,并且无法被其他事件类型所处理
message:同服务器端message事件
anything:同服务器端anything事件
reconnect_failed:重连失败
reconnect:成功重连
reconnecting:正在重连

客户端socket发起连接时的顺序。当第一次连接时,事件触发顺序为:connecting->connect;当失去连接时,事件触发顺序为:disconnect->reconnecting(可能进行多次)->connecting->reconnect->connect。

「两年博客,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏

谢谢你请我吃鸡腿*^_^*

支付宝
微信
0

谢谢你请我吃鸡腿*^_^*

支付宝
微信
标签:

上一篇:

下一篇:

你可能感兴趣

共有 4 条评论 - 基于vue+koa2+socket.io实现多人在线实时聊天

  1. kong Windows NT Chrome 74.0.3729.169

    你好大神,你这个demo有开放的github么?

    1. 管理员 Windows NT Chrome 57.0.2987.98

      @kong在github里koa中,不过没有单独剥离出来

      1. kong Windows NT Chrome 74.0.3729.169

        @管理员还想着拿来学习学习的[捂脸]

        1. 管理员 Windows NT Chrome 57.0.2987.98

          @kong目前文章上面就是剥离的源码呀,只是你需要搭建环境和数据库才能跑起来

博客简介

唯品秀博客: weipxiu.com,一个关注Web前端开发技术、关注用户体验、坚持更多原创实战教程的个人网站,愿景:成为宇宙中最具有代表性的前端博客,期待您的参与,主题源码 

精彩评论

友情链接

他们同样是一群网虫,却不是每天泡在网上游走在淘宝和网游之间、刷着本来就快要透支的信用卡。他们或许没有踏出国门一步,但同学却不局限在一国一校,而是遍及全球!申请交换友链

站点统计

  • 文章总数: 243 篇
  • 草稿数目: 0 篇
  • 分类数目: 15 个
  • 独立页面: 6 个
  • 评论总数: 906 条
  • 链接总数: 14 个
  • 标签总数: 433 个
  • 建站时间: 1070 天
  • 注册用户: 3845 人
  • 访问总量: 8704681 次
  • 最近更新: 2019年11月14日
服务热线:
 173xxxx7240

 QQ在线交流

 旺旺在线