Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输

Posted 雨中风华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输相关的知识,希望对你有一定的参考价值。

by fanxiushu 2022-07-13 转载或引用请注明原始作者。
接续同系列文章:
Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示(二)_雨中风华的博客-CSDN博客_websocket 远程桌面

本来是不该有此文的,只是现实总让理想绕道。
在上一篇文中讲过, xdisp_virt 软件中实现WebRTC渲染之后,在浏览器中看视频和游戏,
已经不再像之前只有WebGL渲染的情况:浏览器远程看视频或游戏,WebGL会让浏览器占用大量CPU等资源。

然而,WebRTC特殊和复杂的底层通信模式, 在些网络环境中让人非常头大。
在一般的局域网和有线内网中,WebRTC问题都不大。头大的是在复杂的互联网环境下。
一旦屏蔽UDP通信,WebRTC基本就凉凉了。
中国的网络,UDP多少会遭到一定程度的限制或者直接屏蔽。
其实这也可以理解,当UDP因为各种P2P软件的原因,造成主干网带宽不断被挤压,
而正常的TCP服务通信遭到威胁的时候,作为运营商,肯定得想办法限制这种情况。
因此这种情况也并不会限于国内,其他国家地区根据情况多少也会有些限制。
还比如有防火墙下,或者公司内部只允许有限的端口上网(比如只开启443,80等端口),这些情况下,webrtc基本也是凉凉。
比如还有些个人情况,有人想用 ngrok等类似的反向代理工具 搭建一个能访问内网的环境,然后想用 xdisp_virt进行远控。
这种情况,webrtc也只能凉凉,现在唯一能用的就只有 xdisp_virt中的WebGL渲染模式,
因为他是通过websocket方式传输数据,能透过 ngrok 的代理模式。
。。。等等,这些情况,在现实网络环境中很多的这类情景。

所以说得难听点,WebRTC只能在理想的网络环境中使用。
于是,牛哄哄的集成各种通信协议的WebRTC,
什么 ICE,STUN/TURN,DTLS,SRTP/SCTP 等等,某些时候好像还真成了玩具。
若不是为了让xdisp_virt能在浏览器中进行远控,其实也不会去实现WebRTC。
实现WebRTC的目的其实就是为了让浏览器能用video标签渲染图像。
发现了WebRTC这种通讯问题之后,尝试着想办法在浏览器端只让WebRTC渲染图像,去掉它的通信功能。
但是好像没办法,反正最终没能成功。

于是再次想到,能不能在浏览器端,用js同时实现 WebRTC的服务端和客户端,
也就是通过常规websocket方式把H264等视频编码数据发到浏览器端,
然后浏览器实现一个WebRTC接收这个数据,作为WebRTC的数据提供者,
然后在同一个浏览器内部,用js再实现另一个WebRTC,用来接收前一个WebRTC的数据并用video标签显示出图像。
虽然这样非常迂回绕道,但是只要能实现,也能解决通信问题。
只是非常可惜,花了许多时间去研究,依然无法实现。

再后来发现TURN协议支持TCP中转传输,是作为 TURN的一个附加协议添加的。
这给WebRTC处理上面的网络问题带来了一点希望,
因为不确定现代浏览器是否都支持 TCP传输的 TURN,于是在linux服务器搭建了coturn服务器。
测试下来,最新版本的chrome, firefox,ios的safari等的WebRTC都支持TCP方式的TURN中转传输,
android因为没设备所以没测试,不过既然与chrome是同一家,应该也支持。

写这篇文章正是基于以上各种网络通信原因,不得不实现 WebRTC中的TCP的TURN中转通信,
从而达到在浏览器端既能使用video标签进行高效渲染,也能很大程度上解决某些受限制的网络环境也能正常使用的问题。
总结起来就一点:在xdisp_virt中实现WebRTC的 TURN的TCP传输。
使用的webrtc依然是开源的亚马逊WebRTC。
不过TCP的TURN服务端是需要自己实现,因为我需要把它同 websocket,http/https等协议集成到同一个端口中,
这样使用起来更加方便。
好在 TCP的TURN协议并不太复杂。

WebRTC的TURN,与我们通常理解的中转服务器,其实没什么本质区别,无非就是实现的协议不同而已。
xdisp_virt实现目标:
在xdisp_virt中实现kvswebrtc(亚马逊的WebRTC),同时实现TCP协议的Turn服务端 tcp turnserver,
kvswebrtc获取视频数据,并且转发到同一个程序内部的tcp turnserver,因为在同一个程序内,它们可以直接使用 127.0.0.1通信。
tcp turnserver同时也接收来自客户端浏览器的连接。
这样tcp turnserver同时中转处理 kvswebrtc和客户端浏览器的webrtc。
而在外面的网络看起来,
客户端浏览器和xdisp_virt之间的WebRTC连接通信都是通过 tcp turnserver,不再像WebRTC默认的行为模式有一大堆的UDP请求。

在kvswebrtc和浏览器中实现WebRTC的 TCP TURN中转,其实挺简单。
在kvswebrtc中,只需在 turn的url中添加 ?transport=tcp ,比如:
turn:127.0.0.1:11000?transport=tcp
并且在 createPeerConnection创建接口的时候设置 ICE_TRANSPORT_POLICY_RELAY 参数即可。
在浏览器中javascript实现与kvswebrtc差不多。如下伪代码:
   ice_svr_addr =
          iceServers: [
                urls: ['turn:192.168.88.8:11000?transport=tcp'],
                  credential: '123456', username: 'turn' ],
          iceTransportPolicy: 'relay'

  pc = new RTCPeerConnection(ice_svr_addr );

经过这样的设置,浏览器的WebRTC 和 kvswebrtc都只会直接连接到 TCP  turnserver服务器端。
当然WebRTC的基本步骤依然没变,依然会收集 sdp,candidate等信息,这些信息依然要通过其他方式发送到对方。
不过candidate信息就只有有关 TURN中转服务器信息,不再包括webrtc所在电脑的信息。

kvswebrtc全程是通过TLS与TURN连接的,而浏览器却很奇怪,好像并没对TCP的TURN TLS提供支持,
反正我把上面的 turn改成 turns,目前所有的浏览器都会报错。
这会带来什么结果,其实也没啥,就是我们用抓包软件能顺抓取识别到 STUN数据包,防火墙想阻止STUN数据包,也能顺阻止。

接着我们再来看看 tcp turnserver的通信过程。
TCP 的TURN协议具体可以查询 RFC6062以及RFC5766等,反正也有好几个RFC文档,
看下来也会觉得挺累的。因此最好的办法是经过实践之后,再来结合RFC文档查看。

好在我们只需实现 TCP 的TURN,而且经过抓包分析,基本上也只需要以下几个命令:
1,ALLOCATE
2, CREATE PERM
3,REFRESH lifetime
4,   BINDCHANNEL
5, INDICATION DATA

1,不管是 kvswebrtc还是浏览器的webrtc,经过相互交换 candidate等信息之后,然后连接到TCP turn server,
第一个请求命令就是 ALLOCATE,意思是请求分配relay port,
实际上就是告诉TURN服务器:请给我留一个位置,好让我与其他连上来的WebRTC进行通讯。
turn 服务端需要检查身份信息,通过查看 是否存在USERNAME和MESSAGE-INTEGARY,
并且做 long-term验证,具体如何验证,可以去查阅 RFC文档。
这里只是简单阐述一下,
每个TURN消息,都有个消息头,消息头大小是 20字节,STUN和TURN消息头的格式都是一样的。如下
/// protocol stun header
struct proto_stun_header_t

    uint16_t   type;
    uint16_t   length;
    uint32_t   cookie;
    uint8_t    number[12];
;
验证办法是 user:realm:password 组成字符串,然后做MD5运算,
运算的16字节作为 HMAC的key,然后把数据包括 TURN头(但是除开MESSAGE-INTEGARY)做 HMAC运算。
运算结果与MESSAGE-INTEGARY属性里的结果进行比较,如果前后一致,说明验证通过。
基本上除了 INDICATION DATA 和 Channel数据之外,基本都需要做验证。

2,分配的relay port有时间限制的,客户端就需要定时发送REFRESH liftetime命令续命。
当lifetime=0表示主动结束连接。

3,当ALLOCATE成功后,其实就可以开始传输数据了,但是为了防止没有授权的乱发。
所以有了 CREATE PERM命令,表示告诉TCP TURN服务端,我接下来需要把数据转发给谁。

4,当 CREATE PERM之后,其实就可以使用 INDICATION 传输数据了。
INDICATION 主要包括两个属性, 一是 XOR-PEER-ADDRESS,表示我这个数据的传输目的站,
二是 DATA,这个就是具体的数据内容了。

5,如果使用 INDICATION传输数据,则会每次都要包含20字节的STUN头和16个字节属性,也就是有36个字节的额外损耗。
因此为了减少这种通讯损耗,采用了 BINDCHANNEL方式。
告诉turn服务器,与某个客户端的数据转发,与2个字节的chanel number绑定。
这样以后每次通信只需要 2个字节的channel number + 2字节数据大小 +数据内容就可以了。
这样把原本需要36个字节的头,减少为只需要4字节的头部。
这样会出现一个问题,如何区分到来的数据包究竟是 常规的STUN头部的数据包,还是 channel number的数据?
好在RFC文档规定,范围在 0x4000-0x7FFF是channel number。
因此我们可以这样判断,先接收2个字节的数据,然后判断是STUN还是 channel number。
然后再做后面的处理。

TCP TurnServer通信内容大致就是这些。
至于更多细节,请查阅RFC,如果查看RFC文档比较头大,可以一边抓包分析,一般查看RFC。

下图是实现了 WebRTC的TCP 方式传输的 xdisp_virt :
有兴趣可以去我的GITHUB下载使用。

 

以上是关于Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输的主要内容,如果未能解决你的问题,请参考以下文章

Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,TCP的TURN中转传输

Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示

Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示

Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示

Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示

Windows远程桌面实现之十二:桌面屏幕通过ONVIF协议与NVR等监控录像设备对接,以及进一步增强直播功能