# 输入事件同步 **本文引用的文件** - [src/index.ts](file://src/index.ts) - [src/websocket.ts](file://src/websocket.ts) - [client/src/inputremoting.js](file://client/src/inputremoting.js) - [client/src/sender.js](file://client/src/sender.js) - [client/src/inputdevice.js](file://client/src/inputdevice.js) - [client/src/memoryhelper.js](file://client/src/memoryhelper.js) - [client/src/pointercorrect.js](file://client/src/pointercorrect.js) - [client/src/signaling.js](file://client/src/signaling.js) - [client/src/peer.js](file://client/src/peer.js) - [client/test/inputremoting.test.js](file://client/test/inputremoting.test.js) - [client/test/inputdevice.test.js](file://client/test/inputdevice.test.js) ## 目录 1. [引言](#引言) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 引言 本技术文档聚焦于输入事件同步机制,围绕以下目标展开:时间同步、延迟补偿、状态一致性保证;事件队列管理、时间戳处理与网络传输优化;缓冲策略、丢包处理与重排序;以及在多设备间实现高精度时间同步与输入一致性。文档基于仓库中的客户端输入遥测与信令通道实现,结合服务端 WebSocket 信令处理,系统性阐述从事件采集、编码、传输到渲染的一体化流程。 ## 项目结构 该项目采用前后端分离的结构: - 客户端包含输入设备抽象、事件打包、信令交互与 WebRTC 数据通道发送等模块; - 服务端提供基于 WebSocket 的信令通道,负责连接管理与消息转发。 ```mermaid graph TB subgraph "客户端" A["输入设备抽象
inputdevice.js"] B["本地输入管理器
inputremoting.js"] C["发送器与事件队列
sender.js"] D["指针坐标矫正
pointercorrect.js"] E["信令(HTTP/WebSocket)
signaling.js"] F["WebRTC Peer 连接
peer.js"] end subgraph "服务端" G["WebSocket 信令服务
websocket.ts"] end A --> B B --> C C --> F C --> E F --> G ``` 图表来源 - [client/src/inputdevice.js:40-89](file://client/src/inputdevice.js#L40-L89) - [client/src/inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [client/src/sender.js:14-188](file://client/src/sender.js#L14-L188) - [client/src/pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292) - [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188) - [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118) 章节来源 - [src/index.ts:13-109](file://src/index.ts#L13-L109) - [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118) ## 核心组件 - 输入设备与状态模型:鼠标、键盘、触摸屏、手柄的状态与事件定义,统一以二进制帧格式封装,便于跨网络传输与解码。 - 本地输入管理器:负责事件采集、时间戳注入、初始布局与设备信息广播、订阅者分发。 - 发送器:将 DOM 事件转换为输入状态,生成状态事件与文本事件,并通过观察者模式推送至数据通道或信令通道。 - 指针矫正器:根据视频显示比例与留边(letterbox)计算屏幕坐标到视频坐标的映射,确保跨设备一致的输入位置。 - 信令层:支持 HTTP 轮询与 WebSocket 两种模式,用于连接建立、SDP 交换与自定义消息广播。 - WebRTC Peer:封装 ICE、Offer/Answer、数据通道创建与状态机,保障媒体与控制面协同。 章节来源 - [client/src/inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178) - [client/src/inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [client/src/sender.js:14-188](file://client/src/sender.js#L14-L188) - [client/src/pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292) - [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188) ## 架构总览 下图展示了从用户输入到远端渲染的关键路径:DOM 事件经由发送器转换为状态事件,通过 RTC 数据通道或信令通道发送,服务端 WebSocket 信令进行路由,最终到达对端渲染端。 ```mermaid sequenceDiagram participant U as "用户" participant DOM as "DOM 事件" participant S as "Sender" participant IR as "InputRemoting" participant DC as "RTC 数据通道" participant WS as "WebSocket 信令" participant P as "Peer(对端)" participant R as "渲染端" U->>DOM : 触发输入事件 DOM->>S : 鼠标/键盘/触摸/手柄事件 S->>S : 生成状态事件与文本事件 S->>IR : 分发事件 IR->>DC : 通过观察者发送二进制消息 IR->>WS : 通过信令通道发送自定义消息 WS-->>P : 转发消息 P->>P : 解析并应用状态 P->>R : 渲染一致状态 ``` 图表来源 - [client/src/sender.js:170-182](file://client/src/sender.js#L170-L182) - [client/src/inputremoting.js:138-168](file://client/src/inputremoting.js#L138-L168) - [client/src/signaling.js:140-146](file://client/src/signaling.js#L140-L146) - [src/websocket.ts:44-114](file://src/websocket.ts#L44-L114) ## 详细组件分析 ### 时间同步与时间戳处理 - 启动时间注入:本地输入管理器在开始发送时记录启动时间,后续所有事件携带相对时间戳,确保跨设备时间基准一致。 - 事件时间戳:输入事件与状态事件均包含时间字段,用于对端按时间顺序重建状态。 - 坐标映射与时间耦合:指针矫正器在事件生成时使用当前相对时间,避免因时间漂移导致的错位。 ```mermaid flowchart TD Start(["开始发送"]) --> SetStart["记录启动时间"] SetStart --> OnEvent["捕获输入事件"] OnEvent --> InjectTime["注入相对时间戳"] InjectTime --> BuildState["构建状态事件/文本事件"] BuildState --> Send["发送至观察者/信令通道"] Send --> End(["完成一次事件发送"]) ``` 图表来源 - [client/src/inputremoting.js:73-91](file://client/src/inputremoting.js#L73-L91) - [client/src/sender.js:170-182](file://client/src/sender.js#L170-L182) - [client/src/pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) 章节来源 - [client/src/inputremoting.js:32-48](file://client/src/inputremoting.js#L32-L48) - [client/src/sender.js:170-182](file://client/src/sender.js#L170-L182) - [client/src/inputdevice.js:149-163](file://client/src/inputdevice.js#L149-L163) ### 事件队列管理与去抖动 - 事件聚合:触摸事件在一次 TouchEvent 中可能包含多个 changedTouches,发送器逐个拆分并生成对应状态事件,保证每个触点独立同步。 - 文本事件与按键事件分离:按键事件区分 keydown/keyup 与重复按下(repeat),仅在非重复场景生成状态事件,避免冗余。 - 去抖动策略:通过事件类型判断与状态缓存(如键盘按键位图、触摸起始时间与起点),减少无效更新。 ```mermaid flowchart TD TStart(["TouchEvent 到达"]) --> Split["拆分 changedTouches"] Split --> ForEach["遍历每个触点"] ForEach --> MapPos["指针矫正器映射坐标"] MapPos --> BuildTouchState["构建 TouchState"] BuildTouchState --> Emit["生成状态事件并派发"] Emit --> TEnd(["结束"]) ``` 图表来源 - [client/src/sender.js:144-151](file://client/src/sender.js#L144-L151) - [client/src/inputdevice.js:478-517](file://client/src/inputdevice.js#L478-L517) - [client/src/pointercorrect.js:20-40](file://client/src/pointercorrect.js#L20-L40) 章节来源 - [client/src/sender.js:130-143](file://client/src/sender.js#L130-L143) - [client/src/inputdevice.js:455-538](file://client/src/inputdevice.js#L455-L538) ### 状态一致性保证机制 - 设备描述与布局广播:首次连接时发送设备列表与布局信息,确保对端具备相同的输入模型。 - 设备变更通知:新增/移除/使用变化等设备状态变更通过专用消息通知,对端据此调整输入映射。 - 状态事件结构:状态事件包含基础输入事件头、状态格式与状态数据,对端按格式解析并应用,保证状态一致性。 ```mermaid classDiagram class InputEvent { +number type +number sizeInBytes +number deviceId +number time +number eventId +buffer() } class StateEvent { +InputEvent baseEvent +number stateFormat +ArrayBuffer stateData +buffer() } class IInputState { +buffer() +format } class MouseState class KeyboardState class TouchscreenState class GamepadState StateEvent --> InputEvent : "组合" IInputState <|-- MouseState IInputState <|-- KeyboardState IInputState <|-- TouchscreenState IInputState <|-- GamepadState ``` 图表来源 - [client/src/inputdevice.js:129-178](file://client/src/inputdevice.js#L129-L178) - [client/src/inputdevice.js:662-718](file://client/src/inputdevice.js#L662-L718) - [client/src/inputdevice.js:180-193](file://client/src/inputdevice.js#L180-L193) 章节来源 - [client/src/inputremoting.js:108-136](file://client/src/inputremoting.js#L108-L136) - [client/src/inputremoting.js:143-162](file://client/src/inputremoting.js#L143-L162) - [client/src/inputdevice.js:662-718](file://client/src/inputdevice.js#L662-L718) ### 网络传输优化与消息封装 - 自定义消息头:消息包含参与者 ID、类型与长度,数据部分为二进制帧,紧凑且跨语言友好。 - 二进制帧格式:输入事件与状态事件均以 DataView/TypedArray 写入,保证小端序与定长字段,降低解析开销。 - 信令通道:支持 HTTP 轮询与 WebSocket,前者适合受限环境,后者提供低延迟与广播能力。 ```mermaid classDiagram class Message { +number participant_id +number type +number length +ArrayBuffer data +buffer() } class NewDeviceMsg { +create(device) } class NewEventsMsg { +create(event) +createStateEvent(device) } class RemoveDeviceMsg { +create(device) } Message <.. NewDeviceMsg Message <.. NewEventsMsg Message <.. RemoveDeviceMsg ``` 图表来源 - [client/src/inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232) - [client/src/inputremoting.js:234-256](file://client/src/inputremoting.js#L234-L256) - [client/src/inputremoting.js:258-277](file://client/src/inputremoting.js#L258-L277) - [client/src/inputremoting.js:279-291](file://client/src/inputremoting.js#L279-L291) 章节来源 - [client/src/inputremoting.js:184-232](file://client/src/inputremoting.js#L184-L232) - [client/src/signaling.js:140-146](file://client/src/signaling.js#L140-L146) - [src/websocket.ts:44-114](file://src/websocket.ts#L44-L114) ### 缓冲策略、丢包处理与重排序 - 缓冲策略:事件以“所见即所得”的方式即时生成并发送,避免大缓冲导致的时延累积;触摸事件按触点拆分,降低单帧体积。 - 丢包处理:当前实现未显式实现重传/确认机制;建议在数据通道上启用可靠传输或在应用层引入序列号与请求重发。 - 重排序:事件按时间戳顺序应用,若出现乱序,可在对端按时间戳队列进行重排与延迟投递,确保状态一致性。 章节来源 - [client/src/sender.js:170-182](file://client/src/sender.js#L170-L182) - [client/src/inputdevice.js:149-163](file://client/src/inputdevice.js#L149-L163) ### 跨设备输入一致性保证 - 指针矫正:根据视频宽高与元素显示区域计算 letterbox,将屏幕坐标映射到视频坐标,消除不同设备显示差异。 - 设备描述:通过设备消息告知对端设备布局与变体,避免按键映射不一致。 - 时间基准:统一使用相对时间戳,结合启动时间校准,减少网络抖动带来的累计误差。 章节来源 - [client/src/pointercorrect.js:78-119](file://client/src/pointercorrect.js#L78-L119) - [client/src/inputremoting.js:117-136](file://client/src/inputremoting.js#L117-L136) ## 依赖关系分析 - 客户端模块内聚度高:输入设备抽象与状态事件解耦,发送器仅负责事件到状态的转换与派发。 - 信令与传输解耦:信令层负责连接与消息路由,数据通道负责高吞吐事件传输,职责清晰。 - 服务端依赖:WebSocket 信令服务集中处理连接生命周期与消息分发,简化客户端复杂度。 ```mermaid graph LR IR["InputRemoting"] --> S["Sender"] S --> ID["InputDevice/IInputState"] S --> PC["PointerCorrector"] S --> OBS["Observer(RTC 数据通道)"] IR --> WS["WebSocket 信令"] WS --> H["WebSocket 处理器"] ``` 图表来源 - [client/src/inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169) - [client/src/sender.js:14-188](file://client/src/sender.js#L14-L188) - [client/src/inputdevice.js:40-89](file://client/src/inputdevice.js#L40-L89) - [client/src/pointercorrect.js:6-124](file://client/src/pointercorrect.js#L6-L124) - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118) 章节来源 - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118) ## 性能考量 - 二进制帧优先:事件以 TypedArray 写入,减少 JSON 序列化成本。 - 小步快跑:事件即时发送,避免批量堆积;触摸事件拆分,降低单帧大小。 - 显示适配:指针矫正一次性计算,避免每帧重复昂贵运算。 - 传输层选择:在低延迟要求下优先使用 WebSocket 信令与 RTC 数据通道可靠传输。 ## 故障排查指南 - 事件未到达对端:检查 RTC 数据通道状态与 readyState,确认观察者是否正确注册。 - 事件乱序或错位:核对时间戳注入逻辑与对端按时间戳重排策略;检查指针矫正参数(视频宽高、元素尺寸)。 - 设备映射异常:确认设备消息已发送且对端已更新设备布局;核对按键映射表与手柄按钮位图写入。 - 信令不通:验证 WebSocket 信令服务运行状态与消息类型路由;检查 HTTP 轮询会话 ID 与轮询间隔。 章节来源 - [client/src/sender.js:202-208](file://client/src/sender.js#L202-L208) - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [client/test/inputremoting.test.js:35-47](file://client/test/inputremoting.test.js#L35-L47) - [client/test/inputdevice.test.js:123-144](file://client/test/inputdevice.test.js#L123-L144) ## 结论 该输入事件同步方案通过“事件即时生成 + 二进制帧封装 + 相对时间戳 + 指针矫正 + 信令路由”的组合,在保证低延迟的同时实现了跨设备的一致性与可扩展性。为进一步提升鲁棒性,建议在应用层引入序列号、确认与重传机制,并对触摸与键盘事件实施更细粒度的去抖与合并策略。 ## 附录 - 测试用例覆盖了消息创建与事件缓冲的基本行为,可作为集成测试基线。 - 建议在生产环境中增加心跳、超时与重连策略,以应对网络波动。 章节来源 - [client/test/inputremoting.test.js:50-132](file://client/test/inputremoting.test.js#L50-L132) - [client/test/inputdevice.test.js:16-173](file://client/test/inputdevice.test.js#L16-L173)