内容简介:笔者最近收到要求出于种种复杂原因,笔者无法使用现在一些市面上很成熟的解决方案。但由于该项目仅限于内部人员使用,即对方案的成熟性,可靠性,可维护性没有太多要求(面部表情逐渐舒展)。远程控制桌面,无论是1对1还是1对多。核心的数据的无非就是俩类。
笔者最近收到要求 远程控制局域网内多N台终端同步操作 的需求(功能类似windows上的远程桌面)。 最终笔者实现了一种 不太成熟,不太稳定 ,但基本满足现阶段需求的方案,且未来会持续迭代。
出于种种复杂原因,笔者无法使用现在一些市面上很成熟的解决方案。但由于该项目仅限于内部人员使用,即对方案的成熟性,可靠性,可维护性没有太多要求(面部表情逐渐舒展)。
分析
远程控制桌面,无论是1对1还是1对多。核心的数据的无非就是俩类。 音视频信息,控制信息 (键盘,鼠标,快捷键等)。当得到这俩个数据时,我们就可以 在控制端实时获取受控端的视频信息以及传输控制指令 。那么接下的操作就是:
1.获取被控制端的视频流
作为一个前端开发工程师,对于如何获取设备的音视频信息这件事,本能的我就想起了 WebRTC (网页即时通讯)协议。
然后就是分析具体的设备和场景是否适用,一顿脑补分析后,我看行(主要是WebRTC本身强大)。
2.发送与接收控制信息
由于具体业务和某些神秘的限定和权衡。采用TCP协议传输消息是不可能的。那没啥好说的了, UDP救我!
虽然他们一次次给我带来的都是, 这个不行,那个也不行 的种种限制,但是好消息也是有的,在一系列复杂的操作后,我拿到了受控方模拟鼠标和键盘操作的接口。得,齐活!
UDP传输不可靠怎么办?
开发初期,笔者遇到的最大的问题就是UDP的传输是无连接的,不保证可靠性,但是在该业务场景下 多个受控端需要对比一段操作后的不同来上报异常 ,如果因为网络的问题导致接收到的指令又略微不同,那 可用性就会大的降低 。
众所周知,UDP的传输是不可靠的,它不像TCP那样保证数据有序,不丢失的传输。但是限制就是在那里,让你头大-.-。 那么问题来了如何 在该场景下确保UDP可靠传输 ?
如何解决该场景下UDP传输不可靠问题
在经过一系列的脑补过后,我的就在 脑海里产生了以下对话 :
(1) 毫无头绪阶段
A:一个服务端控制多个客户端,他们之间怎么传数据? B:UDP组播? A:业务允许丢包吗? B:不允许,而且还要有序执行。 复制代码
总结:该场景下笔者只能使用UDP组播/广播,但又不允许发生丢包。
(2) 提出设想阶段
A:发送端每发一条消息,接收端回服一条确认收到? B:服务端一条消息发出去,然后接受几十条确认消息?如果一秒发送30条数据呢? A: 一台客户端通过组播发送丢包请求后,其他客户端接收到后就不发送相同丢包? B:多台客户端同时丢包怎么办?随机等待时间发送吗?这样做的话,延迟呢?不同步怎么办?而且等待的时间是不是还得监听信道? 复制代码
总结:模仿TCP的确认机制基本不可行的,需要走其他的路。
(3) 初步决定方案A
A:如果客户端不能回复给服务端确定收到的消息,那就只能客户端自己确定丢包? B:对,客户端需要判断自己丢包,然后反馈给服务端! A:客户端唯一的信息来源就是服务端以往发的包,所以就是客户端发的包之间自带联系。 B:客户端发包的时候加上包的Index?然后客户端根据上下俩个包Index是否是连续的来判断是否丢包? 复制代码
总结:客户端发包的时加上递增的Index,服务端通过Index判断是否丢包。
(4) 缓冲区的建立和维护
B: 比如现在服务器下发了包1,2,3后又下发4. 客户端1收到了全部的消息,它保持沉默。 客户端2丢失了信息3,当收到信息4时,它发现前一个包是信息2.于是它就会去给服务器发丢失信息3的反馈包? A: 怎么发?点对点还是走组播? B:走组播,如果其它接收端也丢了这个包,就可以收到了。没丢也可以判断Index来得知这是一个已执行过的包。 同时服务端需要维护一个缓冲区。需要清除已确认发送成功的数据,不然内存会爆。 A:可服务端没办法确定接收端端包是否收到的啊? B:如果丢包的机制生效。当客户端反馈包9丢失,就意味着包9之前的包该客户端都收到了。那就可以把之前的包清除了。 当然也需要设置一个最大值。 A:可是存在多个发送端啊!其中一个收到后,服务端清除该包。一会后另一台客户端又反馈丢了该包,怎么办? B:和已经反馈过收到的客户端要呗! 复制代码
总结:服务端维护一个具有最大长度的缓冲区,并在确认收到后清除无效数据。
(5) 等待和强制执行机制
A:回到刚才,客户端收到信息2后紧接着收到信息4,判断信息3丢了。发送信息3的再次传送请求时,已接收到的信息4怎么办? 如果在等待信息3的过程中,信息5,信息6,信息7都接受到了有如何处理?一直等着吗?还是一个俩个包就忽略丢失? B:绝对不能忽略!因为客户端不知道丢的包是什么操作,如果是点击,忽视会导致客户端之间信息不同步很严重!所以只能等。 同事在收到包后,检测是否有更大Index的包存在。如果有,强制执行该包。 复制代码
总结:客户端同样维护一个缓冲区,丢包时等待同时接受新的数据。收到包后强制执行所有已收到的包。
(6) 多个通道
B:这样的话,所有的客户端收到的消息都是一样的,大家的包Index都是同一个。但是如果此时需要点对点通信怎么办? A:每个客户端都维护一套缓冲区? B:不可能,成本太大。私聊走私聊通道,可以不确保可靠性。 复制代码
总结:服务端需要有组播和点对点通信俩套机制,点对点可以不确保可靠性。
(7) 包加权
A:丢包之后等着回传有可能导致延迟太大,多个客户端不同步。 B:逆推,不需要等 --> 忽视丢包 --> 丢失包不重要 --> 你判断出包不重要。 服务端发包时带上过往的N个包的权重。这样客户端接受到包时就知道前面的包的权重了! A:因为你是通过下一个包来判断上一个包是否丢包的,所以如果下一个包携带之前N个包的重要程度。理论上就可能忽视丢包了! B:所以,需要在发送端给包的权限分等级。比如,鼠标移动等级为1,鼠标点击为10。 复制代码
总结:为了可以做到忽视丢包,需要确定每一个包的权重,做法就是在每一个包里加上之前N个包的权重。
(8) 合包和拆包
A:有一个需求将已段时间的操作存储。然后随时可以一次性全部发送给接收端。 B:一次性发送大的数据会导致包在IP层被分片,包越大被分的片越多。该业务脚本会有多大? A:和录制时长成正比,而且用户操作不可揣测,且不该被限定。所以我们需要在服务端进行手动拆包在客户端进行合包。 B:所以需要给拆分的包加上总包长,该包在总包中的Index等。 A:拆分过的包发送时依然遵循上述可靠协议发送,确保可以收到所有的分包。 复制代码
总结:为了避免在IP层被分片,需要服务端去手动分包,然后在客户端进行合包。
实现过程中遇到的那些问题
问题1 客户端鼠标卡顿
出于对发送信息不能太频繁的考量,鼠标移动的过程中 发送数据做了节流 。代价就是接收端的鼠标收到了一群不连续的的位置点。由于服务端是通过WebRTC显示某一台客户端的画面来进行操作的,所以用户使用体验很差。
解决思路:
如果服务端做了鼠标移动事件节流,那么客户端只需要做鼠标移动插值就行了。即在接受到的俩个鼠标移动事件中,自己计算中间值进行插值。实测优化效果非常明显。
问题2 客户端短时间内反复发送丢包导致服务端负载大
在调试过程中,发现客户端在丢失数据包到接受到该丢失包的过程中,会多次的发送丢包信息给服务端。而后服务端又会在一段时间后返回多条数据(因为收到了多条丢包请求重传的包).
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。