通过WebSocket,实现实时聊天

介绍

WebSocketHTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

总而言之,WebSocket让服务器可以主动向客户端发出请求,实现双向通信,并且可以在浏览器内使用。

image-20220602084816395

使用

服务器

const express = require('express')
const app = express()
const expressWs = require('express-ws') // 引入 WebSocket 包

expressWs(app) // 将 WebSocket 服务混入 app,相当于为 app 添加 .ws 方法

app.get('/', function (req, res) {
  res.send('Hello World!')
})

// 建立 WebSocket 服务
// ws 相当于建立 WebSocket 的实例
app.ws('/ws', ws=> {
  console.log(ws)
  
  // 使用 ws 的 send 方法向客户端发送请求
  ws.send('hello')

  // 使用 on 方法监听事件
  // message 接收客户端的数据
  ws.on('message', function (msg) {
    console.log(msg)
    ws.send('default response')
  })

  // close 事件表示客户端断开连接时执行的回调函数
  ws.on('close', function (e) {
    console.log('连接断开')
  })
})

app.listen(3000);
复制代码

客户端

<script>
  var ws = new WebSocket('ws://localhost:3000/ws');
  ws.onopen = function () {
    console.log('连接成功');
    ws.send('hello');
  };
  // 接收来自服务端的数据,通过e.data获取
  ws.onmessage = function (e) {
    console.log('hello');
    console.log(e.data);
  };
</script>
复制代码

实现一个聊天室

创建项目并初始化

  • 创建 onlineChat
  • 执行 npm init

安装依赖并开启服务器

这里使用express-ws实现webSocket通信

  • npm i express express-ws

  • 开启express服务器

    const express = require('express')
    const app = express()
    const port = 3000
    
    app.use('/', function(req,res){
      console.log('hello');
    })
    
    app.listen(port, () => {
      console.log('server is running');
    })
    复制代码

实现两个聊天页面

设计思路:

  • 顶部:显示对方名称
  • 内容:显示聊天内容
  • 底部:输入框和发送按钮

聊天内容:对方发送的信息显示在左侧,自己发送的信息显示在右侧

我是两个页面分开写(张飞、关羽),这样实现比较简单。

代码

  <div class="main">
    <div class="top">二哥</div>
    <div class="content" id="content">
      <div class="msg left">
        <img src="./guanyu1.png">
        <div>我是二哥</div>
      </div>
      <div class="msg right">
        <div>我是三弟</div>
        <img src="./zhangfei1.png">
      </div>
    </div>
    <div class="bottom">
      <input id="inp" placeholder="请输入内容" />
      <button class="sendBtn" onclick="sendBtn()">发送</button>
    </div>
  </div>
复制代码

此时两个页面已经写好了

效果展示:

image-20220602091036300

我们通过express提供的static获取两个页面

通过 Express 内置的 express.static 可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。

app.use(express.static('public'))
app.use('/guanyu', express.static('public/guanyu.html'))
app.use('/zhangfei', express.static('public/zhangfei.html'))
复制代码

实现数据传递

设计思路:用服务器客户端A发送回来的数据,保存并转发给客户端A客户端B,从而实现聊天功能,客户端B发送的数据也同理。

  • 实现张飞向关羽发送数据

    • 服务器接收到张飞发送的数据
    • 服务器将数据发送给张飞,并存储数据
    • 将存储的张飞数据发送给关羽
  • 代码

    websocket.js

    // zhangfei to zhangfei 简写,表示接收张飞数据,转发给张飞,并存储。张飞调用。下同
    router.ws('/ztz', ws => {
      ws.on('message', msg => {
        zhangfeiInfo.push(msg)
        ws.send(msg)
      })
    })
    
    // zhangfei to guanyu 简写,表示将张飞的数据,发送给关羽。关羽调用。下同
    router.ws('/ztg', ws => {
      setInterval(() => {
        if (zhangfeiInfo.length > 0) {
          let msg = zhangfeiInfo[0]
          zhangfeiInfo.shift()
          ws.send(msg)
        }
      }, 100)
    })
    复制代码

    zhangfei.html

    const ztz = new WebSocket('ws://localhost:3000/ztz')
    ztz.onopen = () => {}
    ztz.onmessage = (res) => content.innerHTML += rightMsg(res.data)
    
    function rightMsg(value) {
        var rightHtml = `
            <div class="msg right">
            <div>${value}</div>
            <img src="./zhangfei1.png">
            </div>
        `;
        return rightHtml;
    }
    
    
    function sendBtn(e) {
        let value = inp.value;
        ztz.send(value)
    }
    复制代码

    guanyu.html

    const ztg = new WebSocket('ws://127.0.0.1:3000/ztg')
    ztg.onopen = () => {}
    ztg.onmessage = res => content.innerHTML += leftMsgFn(res.data)
    
    let inp = document.getElementById('inp')
    
    function leftMsgFn(value) {
      var leftHtml = `
        <div class="msg left">
          <img src="./zhangfei1.png">
          <div>${value}</div>
        </div>
      `;
      return leftHtml;
    }
    复制代码

    到这里就实现了张飞发送数据给关羽

  • 同理实现,关羽发送数据给张飞

  • 效果展示

    onlineChat2

注意:这里获取数据是通过定时器,定时检查服务器存放的数据,如果有数据,那就通过send发送给客户端,所以,定时器设置的时间越短,对方接收的信息延迟就越低。不过时间设置太短容易造成服务器崩溃。

部署到服务器

到这里,项目已经完成了,但是只能在本地运行,我希望能随时随地实现两个设备聊天。

然后正好有个闲置的云服务器,所以我将项目部署到云服务器

  • 进入宝塔面板

  • 将项目上传到文件中image-20220602100141601

  • 使用PM2管理器管理项目

    将传入的node文件添加到PM2image-20220602100421193

  • 设置端口号,启动

    注意:端口号需要提前打开

  • 效果展示

    不同地方的两台移动设备

    image-20220602100903362

总结

如果是http通信,需要通过ajax轮询,让浏览器每隔一段时间,发送一次请求,询问服务器是否有跟新数据,会有很多不必要的请求,浪费服务器资源

WebSockethttp的补充,它不需要通过ajax轮询,只要服务器与客户端建立起连接,服务器就不必等待请求,之间发送数据,减少了资源消耗。

完整代码:github.com/liuxueji/on…

猜你喜欢

转载自juejin.im/post/7104463017555558413