结合SSE实现实时位置展示与轨迹展示

概述

实时位置与实时轨迹的展示是webgis中非常常见的一个功能,本文结合SSE来实现实现此功能。

SSE简介

SSE是Sever-Sent Event的首字母缩写,它是基于HTTP协议的,在服务器和客户端之间打开一个单向通道,服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息,在有数据变更时从服务器流式传输到客户端。

image.png

websockSSE都可以实现在有数据变更时从服务器主动推送到到客户端,他们的比较如下:

  • SSE 是基于HTTP协议的,它们不需要特殊的协议或服务器实现即可工作;WebSocket需单独服务器来处理协议。
  • SSE 单向通信,只能由服务端向客户端单向通信;webSocket全双工通信,即通信的双方可以同时发送和接受信息。
  • SSE 实现简单开发成本低,无需引入其他组件;WebSocket传输数据需做二次解析,开发门槛高一些。
  • SSE 默认支持断线重连;WebSocket则需要自己实现。
  • SSE 只能传送文本消息,二进制数据需要经过编码后传送;WebSocket默认支持传送二进制数据。

由于SSE 单向通信的特性,所以很适合“实时位置与实时轨迹的展示”这样的场景。

实现效果

image.png

实现代码

服务端代码

服务端使用Node通过expressstream实现SSE,实现代码如下:

const express = require('express')
const stream = require("stream");

const app = express()
let sse = null, contentId = 0

function toDataString(data) {
  if (typeof data === 'object') return toDataString(JSON.stringify(data));
  return data.split(/\r\n|\r|\n/).map(line => `data: ${line}\n`).join('');
}

function random(n, m, is = false) {
  let res = Math.random() * (m - n) + n
  const arr = [-res, res]
  if(is) res = arr[Math.round(Math.random())]
  return res
}

let carDict = {}
for (let i = 0; i < 20; i++) {
  const [xmin, ymin, xmax, ymax] = [114.0422270397205295, 22.5261218968098547, 114.0854819347529912, 22.5488829187520494]
  carDict['car'+i] = {
    id: 'car'+i,
    lon: random(xmin, xmax),
    lat: random(ymin, ymax),
    dx: random(0.0001, 0.0005, true),
    dy: random(0.0001, 0.0005, true)
  }
}

app.use(express.static(__dirname + '/web'))

app.get("/sse", (req, res) => {
  res.writeHead(200, {
    "Content-Type": "text/event-stream; charset=utf-8"
  });
  sse = new stream.Transform({ objectMode: true });

  sse._transform = (message, encoding, callback) => {
    if (message.comment) sse.push(`: ${message.comment}\n`);
    if (message.event) sse.push(`event: ${message.event}\n`);
    if (message.id) sse.push(`id: ${message.id}\n`);
    if (message.retry) sse.push(`retry: ${message.retry}\n`);
    if (message.data) sse.push(toDataString(message.data));
    sse.push("\n");
    callback();
  };
  sse.write(':ok\n\n');
  sse.pipe(res);
});

// 触动定时触发
setInterval(() => {
  for (const carid in carDict) {
    const {dx, dy} = carDict[carid]
    carDict[carid].lon += dx
    carDict[carid].lat += dy
  }
  const message = {
    data: carDict,
    event: "dynamicUpdate", // 事件类型,需要客户端添加对应的事件监听
    id: ++contentId,
    retry: 2000,
  };
  sse?.write(message);
}, 1000)

app.listen(18888, () => {
  console.log('express server running at http://127.0.0.1:18888')
})

客户端代码


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容