概述
实时位置与实时轨迹的展示是webgis中非常常见的一个功能,本文结合SSE来实现实现此功能。
SSE简介
SSE是Sever-Sent Event的首字母缩写,它是基于HTTP协议的,在服务器和客户端之间打开一个单向通道,服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息,在有数据变更时从服务器流式传输到客户端。

image.png
websock和SSE都可以实现在有数据变更时从服务器主动推送到到客户端,他们的比较如下:
- SSE 是基于HTTP协议的,它们不需要特殊的协议或服务器实现即可工作;WebSocket需单独服务器来处理协议。
- SSE 单向通信,只能由服务端向客户端单向通信;webSocket全双工通信,即通信的双方可以同时发送和接受信息。
- SSE 实现简单开发成本低,无需引入其他组件;WebSocket传输数据需做二次解析,开发门槛高一些。
- SSE 默认支持断线重连;WebSocket则需要自己实现。
- SSE 只能传送文本消息,二进制数据需要经过编码后传送;WebSocket默认支持传送二进制数据。
由于SSE 单向通信的特性,所以很适合“实时位置与实时轨迹的展示”这样的场景。
实现效果

image.png
实现代码
服务端代码
服务端使用Node通过express和stream实现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')
})
客户端代码