客户端与服务器之间的双向通信

1,920 阅读3分钟

客户端与服务器之间的双向通信

问题:怎么实现同时打开了A页面和B页面,两个页面有共用的数据内容,在A页面请求接口对数据库进行一波操作后,B页面对应相同的数据也要实时更新?

一、方式1

  ajax暴力轮询,代码如下:

<!--B.vue-->
//引用请求库 
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

<template>
...
</template>

export default {
    name: 'B',
    data() {
        return { 
            data: ''
        }
    },
    mounted() {
        let timer = setInterval(()=>{
            this.getData()
        },3000)
    },
    methods: {
        //请求数据
        getData(){
            Vue.axios.get('/api/test')
            .then( res => {
                console.log(res)
                this.data = res.data;
            })
            .catch( error => {
              console.log(error)  
            })
        }
    }
}
ajax轮询图示

  代码中每隔3s向服务器发送请求,获取最新数据后使用。很明显,在数据变化不是很频繁的时候,这种方式的请求有大部分请求是无用的,很大的浪费带宽和服务器资源。对服务端造成了很大压力。

二、方式2

  websocket,是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议,能够实现客户端和服务器实时交互,相互通信.浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输,实现实时数据更新。

websocket通讯图示

  因为原生 websocket 支持到IE11,有兼容问题,实际开发中,常用 socket.io ,它对原始的 API 做了进一步封装和兼容IE处理,提供了更多功能,分为客户端和服务端的实现。下文中客户端使用的是vue封装的一个库 vue-socket.io ,服务器结合 express 引用 socket.io 模块。

使用说明:

  • 客户端
  1. 安装依赖
npm install vue-socket.io --save
npm install socket.io-client --save
  1. main.js中引用
import VueSocketio from 'vue-socket.io';
import socketio from 'socket.io-client';
...
Vue.use(VueSocketio, socketio('http://localhost:3000/'));   //url为服务器请求地址
  1. vue组件使用
<!--SocketTest.vue-->
<template>
...
</template>

export default {
    name: 'SocketTest',
    data() {
        return { 
            ...
        }
    },
    //此处若使用 methods 里方法,需注意this指向问题,可以用this.$options.methods + bind解决
    sockets: {
        //socket连接
        connect: ()=> {
            console.log('client socket connected')
        },
        
        //客户端监听服务器自定义事件serveEmit的响应
        serveEmit: data=> {
            console.log(data)
        },
        
        //断开连接
        disconnect: ()=> {
            console.log("client socket disconnected");
        }
    },
    methods: {
        clickButton: function (data) {
            // $socket代指socket.io-client,客户端向服务器发送clientEmit事件
            this.$socket.emit('clientEmit', { hello: 'I am from client' })
        }
    }
}
  • 服务器
  1. 安装依赖
npm install socket.io --save
  1. serve.js 中使用socket.io
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

server.listen(80);

io.on('connection', function (socket) {
    console.log('server socket connected'); 
    
    //服务器向客户端发送自定义事件serveEmit
    socket.emit('serveEmit', { hello: 'I am from server' });
    
    //服务器监听客户端发送自定义事件clientEmit
    socket.on('clientEmit', function (data) {
        console.log(data);
    });
    
    //断开连接
    socket.on("disconnect", ()=> {
        console.log("server socket disconnected");
    });
});

  不管是服务器还是客户端都有 emiton 这两个函数,可以说 socket.io 的核心就是这两个函数了,通过 emiton 可以地实现服务器与客户端之间的双向通信。

emit :用来发送一个事件,第一个参数为事件名,第二个参数为要发送的数据,第三个参数为回调函数,可省略,如需对方接受到信息后立即得到确认时,则需要用到回调函数。

on :用来监听一个 emit 发送的事件,第一个参数为要监听的事件名,第二个参数为一个匿名函数用来接收对方发来的数据,该匿名函数的第一个参数为接收的数据,若有第二个参数,则为要返回的函数。

  socket.io 提供了三种默认的事件(客户端和服务器都有):connectmessagedisconnect 。当与对方建立连接后自动触发 connect 事件,当收到对方发来的数据后触发 message 事件(通常为 socket.send() 触发),当对方关闭连接后触发 disconnect 事件。此外,socket.io 还支持自定义事件,毕竟以上三种事件应用范围有限,正是通过这些自定义的事件才实现了丰富多彩的通信。

最后,需要注意的是,在服务器端区分以下三种情况:

  • socket.emit() :向建立该连接的客户端广播(一对一)
  • socket.broadcast.emit() :向除去建立该连接的客户端的所有客户端广播(一对多)
  • io.sockets.emit() :向所有客户端广播,等同于上面两个的和(多对多)

应用场景:聊天应用、购物商城、外卖平台等。