Published on

前端实现实时通信的方法?如何选择?

Authors
  • avatar
    Name
    叫我小N就好啦
    GitHub
    @MinorN

前端实现实时通信的方法?如何选择?

首先得知道有哪些通信方法,目前主要有以下几种方法:短轮询、长轮询、SSEWebsocket

短轮询

基本原理

短轮询就是定期向服务器发送请求来获取最新的实时数据,短轮询的特点就是不会保持持久连接

过程:

  • 客户端发送请求
  • 服务端响应对应的请求,如果有新数据,则返回,没有新数据则返回为空或者约定一个空数据的状态码
  • 客户端处理响应
  • 重复请求
  • 循环以上步骤

下面是一个简单的短轮询实例

function loop() {
  setInterval(() => {
    fetch('http://xxx.com/test')
      .then((data) => {
        // 处理数据
      })
      .catch((e) => {
        // 上报错误信息等
        console.log(e)
      })
  }, 5 * 1000)
}
loop()

优缺点

优点:

  • 简单,容易实现,不需要特殊的支持
  • 兼容性好

缺点

  • 效率低,需要频繁向后端发送请求,如果用户量大,可能会增加服务器压力
  • 实时性较差,每次请求都得等待固定时间

总结

总体来说,什么时候使用短轮询呢?一些实时性要求不高的场景,且对服务器压力要求相对较低

长轮询

长轮询是基于短轮询的改进,短轮询不会保持持久连接,但是长轮询向服务器发送的是一个持久连接的请求,当有新数据时,立即返回响应

基本原理

  • 客户端发送请求
  • 服务端处理请求
  • 响应返回,如果服务器有新数据,立即返回响应,并关闭连接;如果没有,则等待新数据到达或者超时
  • 客户端处理响应,需要处理超时的情况,当接收到新数据后,立即再次发送一个长轮询的请求
  • 循环以上步骤

由于需要服务器支持长连接请求,所以需要服务端支持,以下是客户端的简单实现

funtcion loop(){
    setInterval(()=>{
        fetch('http://xxx.com/test').then((data)=>{
            // 这里要处理超时的情况
        }).catch((e)=>{
            // 上报
            console.log(e)
        })
    },1000)
}

loop()

以下是服务端的简单实现

const data = 'init'
app.get('/test', (req, res) => {
  const timeout = 10
  const startTime = new Date().getTime()
  const checkData = () => {
    // 检查数据是否发生变化或特定事件是否发生,这里假设直接使用一个全局变量 data 来模拟数据的变化
    if (data !== 'init') {
      res.json({ data: data })
    } else if (new Date().getTime() - startTime > timeout * 1000) {
      res.json({ data: null }) // 如果超时,则返回空响应
    } else {
      setTimeout(checkData, 1000) // 等待一段时间后再次检查
    }
  }
  checkData()
})

优缺点

优点

  • 实时性好,可以立即返回新数据,同时实现简单
  • 兼容性好

缺点:

  • 资源存在浪费现象,因为长轮询会让服务器维持大量的连接,如果没有数据,必须等到超时才能够关闭
  • 不支持全双工通信,也就是只能服务端向客户端发送信息

SSE

基本原理

SSE 是服务器推送时间,是一种服务器主动向客户端推送消息的技术,SSE 是 HTML5 中一个与通信有关的 API,主要由服务端与浏览器的通信协议和浏览器提供的 EventSource 对象两部分组成,SSE 允许服务端实时地向客户端推送事件流

SSE 很简单,只需要在响应头加上

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

以下是客户端的简单实现

const eventSource = new EventSource('http://xxx.com/test')
eventSource.onmessage = function (event) {
  const data = JSON.parse(event.data)
}
eventSource.onerror = function (error) {
  console.log(error)
}

以下是服务端的简单实现

let clients = []
app.get('test', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('Connection', 'keep-alive')

  const clientId = Date.now()
  clients.push({ id: clientId, res })
  res.write('xxxx') // 初始化消息
  req.on('close', () => {
    // 断开连接
    clients = clients.filter((client) => client.id !== clientId)
  })
})

优缺点

优点:

  • 实时性更加迅速
  • 轻量级,因为基于的是标准的 HTTP 协议
  • 简单易用

缺点:

  • 单向通行
  • 兼容性不如前两种方式
  • 某些情况下,连接可能会断开,需要重新连接

应用场景

  • 即时的通知、提醒
  • 实时数据更新
  • 在线游戏
  • 监控报警
  • 在线交易系统
  • 客服

Websocket

基本原理

Websocket 是一种客户端和服务端之间双向通信的技术,它通过建立一个持久的连接来实现实时、高效的通信

特点:

  • 支持双向通信,可以发送文本、二进制数据
  • 数据格式轻量,性能开销小
  • 没有同源限制

优缺点

优点

  • 实时性高,性能高
  • 跨平台支持
  • 持久连接
  • 双向通信

缺点

  • 长时间连接可能会受到攻击
  • 状态管理复杂

应用场景

  • 在线聊天
  • 实时协作编辑
  • 实时游戏
  • 监控告警