什么是 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 使用起来非常简单。
javascript // 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 {}}).png)





