mock-socket模拟WebSocket接口实战
.png)
mock-socket模拟WebSocket接口实战
SEAlencehe什么是 WebSocket?
以下是来自 DeepSeek 的回答
简单来说,WebSocket 是一种网络通信协议,它允许在单个、长期的连接上进行全双工(双向) 通信。这与我们熟悉的 HTTP 协议形成了鲜明对比。
一个生动的比喻:电话 vs 明信片
HTTP(如明信片): 你每次想获取或发送新信息,都需要寄出一张新的明信片(发起一个新的请求)。客户端问一句,服务器答一句,然后连接就关闭了。如果你想持续获取更新,你就得不停地寄明信片问“有更新吗?”,这被称为 “轮询”。这种方式效率低下,浪费资源。WebSocket(如电话): 你首先拨通一个电话(发起一个 HTTP 握手请求),电话接通后(WebSocket 连接建立),你们之间就保持了一条持续的通信线路。双方可以随时说话,你来我往,无需挂断再重拨。这实现了真正的实时、低延迟的对话。
WebSocket 的核心特点
全双工通信
服务器和客户端可以同时、独立地向对方发送数据。客户端无需等待服务器的响应,反之亦然。单一 TCP 连接
连接一旦建立,在整个会话期间都会保持打开状态。这消除了为每个数据交换建立新连接的开销。低延迟
由于没有频繁建立连接和发送冗余 HTTP 头信息的开销,数据可以非常快速地在双方之间传递。真正的实时性
服务器可以在有新数据时主动“推送” 给客户端,客户端无需反复询问。WebSocket 是如何工作的?
WebSocket 连接的建立依赖于一次普通的 HTTP 请求,这个过程叫做“握手”。握手(Handshake)
客户端(例如浏览器)发送一个特殊的 HTTP 请求,其中包含一个头信息 Upgrade: websocket。
这相当于客户端对服务器说:“你好,我们别用 HTTP 了,升级到 WebSocket 协议聊天吧?”
如果服务器支持 WebSocket,它会回复一个 HTTP 101 Switching Protocols 的响应,表示同意升级。
至此,握手完成,底层的 TCP 连接保持不变,但通信协议从 HTTP 切换到了 WebSocket。
数据交换
握手成功后,连接保持打开状态。
双方开始使用 WebSocket 协议定义的数据帧格式进行通信,可以发送文本或二进制数据。
连接关闭
当任何一方决定结束通信时,可以发送一个关闭帧来优雅地终止连接。
为什么需要 WebSocket?解决了什么问题?
在没有 WebSocket 之前,为了实现实时效果,开发者们使用了一些“曲线救国”的方法:轮询: 客户端每隔几秒就向服务器发一个 HTTP 请求问:“有数据吗?” 无论服务器有没有新数据,都会响应。这种方式效率低,浪费带宽和服务器资源。
长轮询: 客户端发起一个请求,如果服务器没有新数据,就把这个请求挂起,直到有数据或超时才返回。客户端收到响应后立即再发起一个新的请求。这比普通轮询好一些,但依然复杂且对服务器有压力。
WebSocket 的出现,正是为了从根本上解决这些方法的低效和复杂性,为真正的实时双向通信提供了标准方案。
主要应用场景
WebSocket 非常适合需要高实时性和频繁数据交换的场景:在线聊天/即时通讯:最典型的例子,消息即发即收。
多人在线游戏:玩家的每一个操作都需要实时同步给其他所有玩家。
实时数据仪表盘:股票行情、体育赛事比分、实时销售数据、服务器监控等。
协同编辑工具:如 Google Docs,多人同时编辑时,能看到彼此的改动。
在线直播弹幕和点赞。
代码示例(前端 JavaScript)
在浏览器中,WebSocket API 使用起来非常简单。// 1. 创建 WebSocket 连接,连接到服务器端点 const socket = new WebSocket('ws://example.com/socket-server') // 2. 监听连接打开事件 socket.onopen = function (event) { console.log('WebSocket 连接已建立') // 连接建立后,发送一条消息给服务器 socket.send('你好,服务器!') } // 3. 监听来自服务器的消息 socket.onmessage = function (event) { console.log('收到服务器消息:', event.data) // 可以更新网页上的内容,例如将消息添加到聊天窗口 } // 4. 监听连接关闭事件 socket.onclose = function (event) { console.log('WebSocket 连接已关闭') } // 5. 监听错误事件 socket.onerror = function (error) { console.error('WebSocket 错误:', error) } // 发送消息的函数 function sendMessage(message) { if (socket.readyState === WebSocket.OPEN) { socket.send(message) } }总结
特性 HTTP WebSocket 通信模式 半双工(请求-响应) 全双工(双向) 连接 短暂,每次请求后关闭 持久,单一长连接 开销 每次请求都包含完整的 HTTP 头 初始握手后,数据帧头很小 实时性 靠轮询模拟,延迟高 真正的实时,低延迟 数据流向 主要由客户端发起 服务器可以主动推送 总而言之,WebSocket 是为现代 Web 应用对实时性、低延迟的高要求而设计的通信协议,是构建互动式、实时 Web 应用的基石技术。
简而言之,就是不用再向服务器发起轮询请求(在吗?有消息吗?),只用建立一次链接,之后就可以高效的双向通信。
因此,在实际开发中,前端页面要显示某个设备(如无人机,车辆)在地图上的实时位置,WebSocke(以下简称 ws),就显得很有用了,节省了大量资源和性能开销。
那么,作为一个 webGis 前端开发者在构建 cesium 应用的时候,后端的 ws 接口还没有准备好的时候,前端开发怎么模拟 ws 接口呢?首先容易想到的是 mockjs,很遗憾,mockjs 无法直接模拟,它的核心功能是拦截 ajax 请求并生成模拟数据。
mock-socket 介绍和使用
这里就要介绍 mock-socket 这个包了,这是它的 GitHub 主页
mock-socket 的简单使用
主页有安装和 hello world 的起步,这里就不赘述了
如何模拟设备的定位信息呢?
定位数据的准备
这里给出一个 json 数据的简单示例
{
"locations": [
{
"time": "2025-01-01 10:01:00",
"location": {
"type": "Point",
"coordinates": [117.627915, 40.608494, 731.15]
}
},
{
"time": "2025-01-01 10:02:00",
"location": {
"type": "Point",
"coordinates": [117.627915, 40.608494, 731.15]
}
},
{
"time": "2025-01-01 10:03:00",
"location": {
"type": "Point",
"coordinates": [117.627915, 40.608494, 731.31]
}
},
{
"time": "2025-01-01 10:04:00",
"location": {
"type": "Point",
"coordinates": [117.627915, 40.608494, 731.48]
}
},
{
"time": "2025-01-01 10:05:00",
"location": {
"type": "Point",
"coordinates": [117.627915, 40.608494, 731.59]
}
}
]
}
引入 mock-socket 实例
首先创建启动的函数,在启动的函数里面处理准备的模拟数据 json,把组织好的数据每五秒进行一次广播
import { Server } from 'mock-socket'
// mock-socket 的 Server 实例;复用以保证多次启动不会重复创建
let server = null
// 定时广播的计时器句柄
let broadcastTimer = null
// 启动本地位置 WS 模拟服务器(若已启动则返回停止函数)
export const startMockLocationWsServer = async () => {
if (server) {
return stopMockLocationWsServer
}
// 从 public 目录加载模拟位置数据
const response = await fetch('/geojson/mockLocation.json')
if (!response.ok) {
throw new Error(`加载 mockLocation.json 失败: ${response.status}`)
}
const json = await response.json()
// 期望结构:{ locations: Array<FeatureLike> }
const locations = Array.isArray(json?.locations) ? json.locations : []
if (locations.length === 0) {
throw new Error('mockLocation.json 中未找到有效的 locations 数组')
}
// 创建本地 WS 服务,并维护一个已连接 socket 的集合
server = new Server(MOCK_WS_URL)
const sockets = new Set()
server.on('connection', (socket) => {
sockets.add(socket)
const handleClose = () => sockets.delete(socket)
socket.addEventListener?.('close', handleClose)
socket.onclose = handleClose
})
let index = 0
// 广播函数:按序从 locations 取一条记录并推送给所有连接的客户端
const broadcast = () => {
try {
const record = locations[index % locations.length]
const payload = {
type: 'location',
index: index % locations.length,
time: record.time,
lng: record.location.coordinates[0],
lat: record.location.coordinates[1],
height: record.location.coordinates[2],
}
const data = JSON.stringify(payload)
sockets.forEach((s) => {
try {
s.send(data)
} catch {}
})
index += 1
} catch {}
}
// 立即推送一条,随后每 5s 推送一次
broadcast()
broadcastTimer = window.setInterval(broadcast, 5000)
return stopMockLocationWsServer
}
停止的函数
export const stopMockLocationWsServer = () => {
if (broadcastTimer != null) {
clearInterval(broadcastTimer)
broadcastTimer = null
}
if (server) {
try {
server.stop?.()
} catch {}
server = null
}
}
可以看到,控制台已经按照预期的每五秒钟打印出位置信息
记得 cesium 组件在卸载时候一并关闭 ws
onBeforeUnmount(() => {
try {
ws?.close?.()
} catch {}
})







