WebRTC传输基本知识
WebRTC首先要解决的是两个浏览器之间如何实现音视频的实时互动,对于底层来说就是如何实现两个端点之间高效的网络传输,为了解决这个问题WebRTC引入了很多的传输协议
NAT (Network Address Translator)
下面我们以传统的邮件作为例子,给大家说明NAT是什么?比如A和B两个人要发信,那B告诉A它在某个楼的某层时,这个时候A 可以给B发消息或者信件吗?这肯定不行,因为它并不知道一个具体的地址是多少?你必须告诉它具体哪个省哪个市哪个区哪个小区哪号楼哪层时,只有这种公共的地址,也就是大家都认识的地址,邮局才能帮你把这封信送达。你说哪号楼哪层这个只有你小区内的人才知道。那这个就和我们的网络是相关的。
那对于网络上的主机,你必须要有个公网的地址,那相互之间才能进行通讯,如果告诉它一个私网(内网)的地址,那它根本 找不到你。那对于我们现实中大部分主机都是在网关之后的,他们之间都是有自己的内网IP地址,并不知道自己的外网是多少。那怎么办呢?实际是有一个映射,在网关上有个NAT功能,它可以使你的内网地址变成外网地址。所以他就是一个资源组,映射之后就将你的内网IP端口映射成外网IP端口。那有了外网的IP端口之后,其他的主机就可以通过内网的IP地址与你通讯了。这就是NAT。NAT的穿越和类型我们后面在说。
STUN(Simple Traversal of UDP Through NAT)
有了NAT之后,可以将内网地址转成公网地址,那两个公网之间是不是就可以通讯了呢?那中间还是缺了一步的,他们虽然 都存在这个世界上,但是彼此并不认识,怎么办呢?那必须要有一个第三方的服务做一个介绍,这个就是STUN服务。
STUN服务说白了就是做一个中介,把各自的公网信息进行一下交换,让他们彼此进行认识,这个STUN服务也非常简单。
TURN(Traversal Using Relays around NAT)
经过介绍认识之后,A和B这两台主机就可以建立连接了,连接一旦建立完毕就可以传输数据,那光有STUN服务他们之间是不是就一定能够创建成功这个连接呢?其实不一定,在美国有一项数据表示在进行P2P穿越的时候,有70%是可以穿越成功的,但是实际上在国内来说就很难达到这个70%的成功率,50%可能都到不了。那在现实过程中,我又要实现浏览器之间的传输,那当P2P连接不成功的情况下,如何保证音视频还能互通呢?
这就引入了TURN服务,TURN 就是在云端架设一个服务器,这个服务器就负责之间双方流媒体数据的转发,让他们进入到同一个房间里之后呢,这个TURN就会给房间里的所有人进行转发,那么对 端就能收到了,A 发送信息通过TURN到了B,同样的B发送信息通过TURN发送给A。这样就在P2P连接不成功的情况下 ,它有了一条路线可以进行音视频的传输。这就是TURN服务。
ICE (Interactive Connectivity Establishment)
ICE就是将上面介绍的NAT、TURN等服务打包一起做一个最优的选择,那它首先尝试进行P2P,P2P在你的主机上有可能有双网卡或者是多个端口,当其中有一个端口或者某一个网卡不通的时候,它可以换 其他 的,如果两条都是通 的时候,它选择一条更高效的,也就是说哪个网卡性能更好它会使用哪个。那当P2P不通的时候它又会选择TURN服务中转,TURN也不一定能通,尤其是中国,很有可能被拦掉,那怎么办呢?那有可能选择了多个节点,有可能是在上海一个节点,在日本东京一个节点,当上海的节点不通的时候还可以 选择东京的节点,ICE就是将这些所有的可能性都罗列好,会在这其中找到一条最优的路径,将数据传送过去。
1 NAT
首先是NAT,这张图就表现的非常清楚,这就是一个地址映射,左边的分别 是内网的几台机子,通过内网的IP他们之间是可以相互通信的,但是与互联网之间是不通的,那么如何访问互联网的资源呢,那就必须通过NAT,将我们的内网地址转换成外网地址。
那么由于每台主机都要映射不同的端口,NAT产生的原因有两种,一种是IPv4的地址不够,解决IPv4地址不够有两种方案,其中最好的是使用IPv6,IPv6的地址池更多,基本上每台主机都有自己的IP地址。还有一种就是进行NAT穿越,就是内网数万台主机都有自己的IP地址,但是映射到外网只有一个IP地址或者几个IP地址,它通过端口好来区分每一台主机,那就形成了一对几百或者以对几万,大大减少了公网IP地址的使用。第二个是处于网络安全的考虑。
NAT种类
NAT一共有四种类型,分别是
完全锥型NAT (Full Cone NAT)
地址限制锥型NAT(Address Restricted Cone NAT)
端口限制锥型NAT (Port Restricted Cone NAT)
对称型NAT (Symmetric NAT)
对NAT穿越来说,其实每一种穿越都不一样,下面我就来看一下每一种不同类型,如何进行NAT穿越。
完全锥型NAT
完全锥型是非常简单的 ,左边是内网的主机,它有自己的内网IP地址和端口 ,通过防火墙之后,它形成一个外网的IP地址,那么外网的三台主机要想与内网的主机进行通信的时候,首先要由内网的主机向外发送一个请求,请求外网中的其中一台主机,这样会形成的结果就是它会在NAT服务上打 一个洞,这样会形成一个外网的IP地址和端口,那么形成了外网的IP地址和端口之后,其他的主机只要获得了这个IP地址和端口它都可以向它发送数据。并且可以顺利的通过防火墙发送给内网的主机。这样就可以进行通讯了,这是完全锥型,也是最好穿越的一种 NAT类型。但是安全性就差很多。
地址限制锥型NAT
它的安全性好一些,它会在防火墙上形成一个五元组,就是内网主机的IP地址和端口和映射后的公网IP地址和端口以及我要请求的这个主机IP地址,他们首先有一个公共的步骤,第一步就是要先由内网的主机向外网发送一个请求,在这个防火墙上或者NAT服务上形成一个映射表,那形成之后外网的主机就可以和内网的主机进行通讯了。
如图所示,它首先向P的主机发送请求,那么P就可以通过不同的端口向内网的主机发送消息它都是可以接受到的,但是对于其他主机来说,由于IP地址的限制,它返回来的时候,一看IP地址不对,就会被拦掉。只有P的主机是可以通过的,而且它的各个主机都可以跟它进行通讯。这就是地址限制型,这个地址限制型的打通,首先就是这个内网主机无论跟哪个主机进行打通,首先它都要发送 一个请求,发送请求之后就形成了所谓的映射表在我们的网关上。其他主机就可以给它通讯了。这是地址限制型NAT。
端口限制锥型NAT
端口限制型就更加严格一些了,不光是对IP地址,还要对端口做限制,那所以在这个防火墙上就形成了六元组,不光有内网的IP地址和端口以及映射后的公网的IP地址和端口,还有你请求的主机的IP地址和端口,那么在在这种情况下P这台主机,它发送消息的时候,如果请求的是P这台主机的这个q这个端口的服务,只有它这个服务才能返来,其他的端口(如:r端口)发送数据就不行了。
那如果内网的主机没有向S这个主机发送请求的话,S主机发送信息到内网的主机是肯定不通的;但是如果内网的主机给M这台主机的n端口已经发送了请求,那么M主机的n端口也是可以打通这个数据防火墙然后与这个内网主机进行通讯的。这就是端口限制锥型NAT。
对称型NAT
对称限制型就更加严格了,以前的类型是在防火墙上形成映射后的公网的IP地址是保持不变的,大家要找还是能找到它的,虽然不 通,但是对于 这个对称型它就不一样了,它就发生了变化,不光是形成了这个一个IP地址和端口,而且还会形成多个,对于每一台主机都会形成一个不同的IP地址和端口对,所以这个 时候当内网主机给Pq发送请求的时候,Pq可以回来信息,其他的都回不来。但是给M主机 n端口发送数据的时候,又形成一个C和d,这个M,n在发数据的时候不会像A,b发送数据,必须向C,d发送数据 才可以过来,这样才能进行互通,这个就是对称型的NAT穿越。这个就是对称型的NAT穿越。
NAT穿越的基本步骤
NAT穿越原理
1 需要知道C2地址, C2需要知道 C1地址,因此C1,C2向STUN发消息,STUN获取到C1 C2的公网信息
2 STUN 交换公网IP及端口,将C1 C2的信息发送给对方
3 按照类型进行打通
我们要进行穿越其实是两台主机直接进行穿越,也就是C1,C2之间进行穿越。C1和C2之间进行穿越首先C2要知道C1的地址,C1要知道C2的地址,那就要通过STUN服务发送消息,STUN收到他们的消息之后就会拿到它们对应的公网的IP和端口;所以这个时候要进行信息交换,就是将C1的公网IP和端口交给C2,将C2的公网IP和端口交给C1;交换完这些信息之后,我们就要按照类型进行打通,如果是完全锥型的,他们就直接可以进行通讯了,已经具有公网的IP和端口了。
对于完全锥型,只要我在防火墙上建立一个映射,那任何一台主机,如果我们把STUN当作一台主机的话,那C2就可以通过C1和STUN之间的这个公网的映射然后去发送数据。
对于IP地址限制锥型,STUN 不能用这个IP地址给这个C1发消息,那因为C1是知道C2的公网IP和端口,所以首先内网主机C1向C2发送一个请求 ,C2在利用C1形成的这个IP地址和端口给它返回数据,这样他们也是可以互通的。
对于端口限制型其实也一样的,都可以通过这种方式。
C1->C2,C2->C1,甚至是端口猜测
那么对于对称型NAT就比较麻烦了,对于对称型NAT来说,由于是IP地址和端口的变化 ,那更多的是端口的随机性和增长,就是说线性增长的变化,所以并不能直接进行互通,那怎么才能通呢?其实也是有办法的,就是通过端口猜测的方式,就是通过几次的探测,然后找到它端口分配的规律,它是线性增长的,比如每次增1,或者每次增2等,还是一个 随机的,随机数是在 一定范围的,不可能所有端口都随机,在这些随机的端口范围内,实际它每一个都做一次尝试,那么这个时候就有可能打通,那么这种打通的概率就低很多。
这个就是整个NAT穿越的基本原理。
下面我们再来看一看组合
哪些是可以穿越成功,哪些是不成的,这里有一张表 ,那通过上表我就可以 知道哪些是可以通过P2P打洞成功的,哪些是完全不能打通的,当我们检测到一端是端口受限锥型一端是对称型或者两端都是对称型,那肯定是不可以打通的,肯定是不可以的。
NAT类型的检测
看看我们自己是属于哪种NAT类型,是否可以打洞 成功,下面我就看一下这个整体的判断逻辑,当然在这之前我要有一个限定条件,就是在我们的云端一定要部署一个STUN服务。这个STUN服务要有两个IP地址和端口,这两个IP地址的作用稍后我们会在逻辑判断的过程中给大家介绍。
T1=>首先客户端要发送一个ECHO请求给服务端,服务端收到请求之后会通过同样的IP地址和端口在给我们返回一个信息回来。
Received?=>那在客户端就要等这个消息回复,那么设置一个超时器,看每个消息是否可以按时回来,那如果我们发送的数据没有回来,则说明这个UDP是不通的,我们就不要再进行判断了。
YES->Public IP =>如果我们收到了服务端的响应,那么就能拿到我们这个客户端出口的公网的IP地址和端口,这个时候要判断一下公网的IP地址和本机的IP地址是否是一致的,如果是一致的,说明我就没有在NAT之后而是一个公网地址;
YES->NOT NAT=>接下来要做进一步判断,就是判断我们的公网地址是不是一个完全的公网地址,T2=>这时我们再发送一个信息到第一个IP地址和端口,那服务端收到这个请求之后呢,它使用第二个IP地址和端口给我们回消息,Recieived?=>如果我们真是一个完全的公网IP地址和端口提供一个服务的话,那其他任何公网上的主机都可以向我发送请求和回数据,这时候我都是能收到的,那如果我能收到,Open Internet=>那就说明就是一个公网的地址,所以我们就没有在NAT之后就完全可以接收数据了。
No->Symmetric FireWall=>那如果我们收不到,那说明我是在一个防火墙之后,而且一个对称的防火墙。如果我收到的公网的IP与我本地的IP不一致,那就说明我们确实是在NAT之后,那既然是在NAT之后我们就要对各种类型作判断了。
NO->NAT detected=>NAT 这时我们再发送一个请求到第一个IP地址和端口,那它是通过第二个IP地址和端口给我们回消息,那这时候我们要判断我们的类型是不是完全锥型,如果我们出去一个请求,在我们的NAT服务和网关上建立了一个内网地址和外网地址的映射表之后,那其他公网 上 的主机都可以向我这个公网IP地址发送消息,并且我可以接收到,那么这个时候可以收到的话,我们就是一个完全锥型NAT。
那么如果收不到的话,需要做进一步的判断,这时候需要向服务端的第二个IP地址和端口发送数据,那么此时服务端回用同样的IP地址和端口给我们回数据,那么这时候它也会带回一个公网的IP地址来,但是如果我们的出口,就是向第二个IP地址发送了请求带的出口的IP地址与我们第一发送的请求带回的IP地址如果是不一样的,那就说明是对称型NAT;对称型NAT每次出去都会形成 不同的IP地址和端口。
如果一样就说明是限制型的,限制型分为两种一种是IP限制型一种是端口限制型,所以还需要做进一步的检测,所这个时候我们再向第一个IP地址和端口发送一个请求,那么回信息的时候是同一个IP地址和不同的端口号,那么这时候我们就可以判断是否可以接收到,如果不能接收到,说明是对端口做了限制,所以是端口限制型的NAT,如果可以收到就说明是一个IP地址限制型的NAT。
经过这样一个逻辑判断之后 ,我就可以知道我们自己这台在内网的主机是什么NAT类型了。
了解了逻辑之后,我们再来看一下检测过程:
首先是我们的客户端,通过第一IP地址和端口,发送一个请求过去回来一个响应,如果回来这个地址和 我们之前发送的地址是一致的,那就是公网的。如果不一致说明我们是在NAT之后,这是第一次检测。
再接下来就是向第一个IP地址发送了一个请求 ,然后它通过第二个IP地址给我回一个请求,如果这次回来的IP地址与上次回来的IP是不一样的,它就是对称型NAT;如果一样还需要进一步判断,
紧接着再发送一个请求到第一个这个地址,那么它用这个 地址的第二个端口向我回消息,如果这时候我是能收到的,说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
以上就是我们NAT检测基本的一个过程。
2 STUN协议
STUN存在的目的就是进行NAT穿越
STUN 存在的目的就是进行NAT穿越,NAT有四种类型,每种类型如何穿越它的基本原理是什么,都是属于STUN协议中的一部分。
STUN是典型的客户端/服务器模式。客户端发送请求,服务端进行响应
RFC STUN规范
RFC STUN规范中,实际上有两套STUN规范
第一套叫做RFC3489/STUN
Simple Traversal of UDP Through NAT
它就是将STUN定义成简单的通过UDP进行NAT穿越的一套规范,也就是告诉你如何一步一步通过UDP进行穿越,但是这套规范在穿越的过程中还是存在很多问题,尤其是现在的网络路由器对UDP的限制比较多,有的路由器甚至不允许进行UDP传输,所以这就导致了我们通过RFC3489这套规范进行NAT穿越的时候它的失败率会非常高。所以为了 解决这个问题,又定义了另一套标准,RFC5389.
第二套叫做RFC5389/STUN
Session Traversal Utilities for NAT
RFC5389是在RFC3489的基础上又增加了一些功能,但是它对整个STUN的描述就不一样了, 它是把STUN描述成一系列穿越NAT的工具,所以都叫STUN,但是他们的含义完全就不一样了。RFC5389在UDP尝试可能失败的情况下,尝试使用TCP,也就是说RFC5389是包括UDP和TCP的两种协议进行NAT穿越的,这是两套规范最本质的区别。当然在协议的具体内容上,包括协议头还有协议体中的属性都有很多的变化,但是那些都不是最关键的,最关键的是RFC5389里面将TCP纳入进来。你可以通过TCP进行穿越。
这是STUN 的RFC的两套规范。大家一定要清楚,尤其是查文档的 时候一定要清楚查询的是哪一套规范。
下面我们就具体看看这个STUN协议
包括20字节的STUN header
Body中可以有0个或多个Attribute
STUN这个协议它是包括了消息头和消息体,消息头是20字节固定的消息头,Body中可以有0个或者多个Attribute属性,后面我们会 介绍属性的作用。
那么这20字节头是由哪些组成呢?
其中:
2个字节(16bit)类型
2个字节(16bit)消息长度,不包括消息头
在网络协议中有很多的协议规范,它的这个消息头中都有长度,有的是包括这个头的有的是不包括这个头的,这个大家一定要记清楚 。
对于STUN来说,消息头的长度length是不包括消息头的;
16个字节(128bit)事务 ID,请求与响应事务ID相同
第三个是事物ID,它是16字节或128bit组成,它的作用就是请求和响应事物是相同的ID,用于请求与响应的匹配的;比如我客户端 发送了好几个请求,那服务端对于每一个请求都要返回一个响应 ,那怎么知道某个响应是对应到的请求呢,就通过这个事物ID。
如果它事务ID号相同,说明这两是匹配的,整个逻辑就知道怎么做了,否则的话就很难判断。所以它有三部分组成,第一是两个字节的类型,第二是两个类型的消息长度,不包括这个消息头,第三个是16字节的128位的事物ID,请求与响应的匹配。这个是消息头。
我们再来看消息头的格式
上图的格式是最新的RFC5389的格式,刚刚我们上面说三个的是RFC3489,那么RFC5389和RFC3489之间有什么区别呢?
首先我看消息类型,消息类型的最低两位必须是 0 0,就是RFC5389最新的协议 ,这是第一点的不同;第二点的不同是,事物ID,老的里面是128位事物ID,在新的里面是 96位,其中有32位单独划出来了单独作为 Magic Cookie,一个魔法树,这就是RFC3489和5389的STUN消息头的区别。
下面我们再来看每一项,首先是这个Massage Type
STUN Message Type
前两位必须是00,以区分复用同一端口时STUN协议
就是不同的协议复用同一个端口的时候,用它来区分哪个是STUN协议哪个不是STUN协议,这是两位00的作用。
2位用于分类,即C0和C1
剩下的14位中有两位用于分类,就是C0和C1,C0和C1是占两位,所以它有四种类型,也就是四个分类,第一种是请求,第二种 是指示,第三种是成功,第四种是错误应答,所以它将Message Type消息类型分成了四类。
12位用于定义请求/指示
剩下的12位是用于不同请求的定义,比如1代表绑定,2代表私有消息隐私数据,……,但是在STUN 3489里面定义了两个,就是用 这12位定义了两种,但是5389里面就一种,一种就够了。下图就是STUN Message Type的结构。
14位其中C0和C1分别在这个位置,两个不是放在一起的,它是隔着的,隔着有什么好处,就是按照16进制的话,我们4位为一组,
所有的M代表的是请求的值,C是分类,这样划分,四个一组,第一组 里面全都是请求,都是消息类型,第二组里面代表分类,第三组里面又代表另外一种分类。所以这两个进行不同的组合就能分成不同的类型了。
我们来看 C0C1
C0C1
C0C1第一个0是C0 第二个0是C1。因为这个C1是处于这个高字节,大端存储
0000 1011 0000 0000
0b00:表示是一个请求
0b01:表示是一个指示
0b10:表示是请求成功的响应
0b11:表示是请求 失败的响应
所以这里我们只要记住成功响应和失败的响应就好了。
下面我们就可以看一下STUN的消息类型,它一共定义了6个消息类型
这个就是STUN的消息类型,但是RCF5389就将这个私密类型去掉了。就剩下一个 ,那完全够用了。
在下面就是大小端模式
对于这部分类型,为什么要了解呢,就是后面要对C0和C1要做详细的解释。
大端模式:数据的高字节保存在内存的低地址中
小端模式:数据的高字节保存在内存的高地址中
网络字节顺序:采用大端排序方式,高字节时最重要的信息,先发送,然后存储在内存的低地址中
大端存储:M0是低字节,00是最高字节
Transaction ID
4字节,32位,固定值0x2112A442. 通过它可以判断客户端是否可以识别某些属性
新的RFC5389将它分成了两部分,前四个字节也就是32位是一个magic cookie,magic就是一个魔法树了,在这里是固定的0x2112A442,所以我们看到固定的值是这个的话,就是RFC5389否则就是RFC3489。
通过它是可以识别客户端是否可以识别某些属性,因为不同的规范属性实际上是有变化的,所以你看到 这个魔法树之后就是RFC5389, RFC5389定义了一些新的属性,如果不是的话,这些新的属性就可以忽略掉。
12字节 ,96位,标识同一个事物的请求和响应
剩下的12个字节,96位,标识同一个事物的请求和响应,当我们客户端发送多个请求的时候,就能通过这个事物ID将服务端返回的响应与之前发送的请求进行一一匹配。
下面在看STUN Message Body 消息体
消息头后有0或多个属性
每个属性都使用TLV(动态)编码:Type,Length,Value
Value这个值是可以变化的,变化的程度怎么知道有多长呢?通过这个Length,这个Length标示了Value的长度,最终的消息是一个32位对齐的,如果最后不是32位对齐,要通过补0来达到对齐,这是整个Body。
3 TURN协议
其目的是解决对称NAT无法穿越的问题
在之前我们介绍过,NAT的四种类型(完全锥型NAT (Full Cone NAT)、地址限制锥型NAT(Address Restricted Cone NAT)、端口限制锥型NAT (Port Restricted Cone NAT)、对称型NAT (Symmetric NAT))。
当我们检测到一端是端口受限锥型一端是对称型或者两端都是对称型,那肯定是无法穿越的;在NAT无法穿越的时候,我们如何才能保证业务的运行呢?那这个时候就要引入TURN协议,TURN协议实际上就是在服务端架设一个TURN服务,客户端在发送数据的 时候无法NAT穿越的时候将这个媒体流数据首先传给TURN服务,通过TURN的中介然后转给其他的接收者,或者其他接收者也可以发送数据给这个TURN服务,TURN在转给client端。这就是TURN 出现的目的。
其建立在STUN之上,消息格式使用STUN格式消息
上次我们说了STUN协议的头和body是什么样子的,那TURN协议就是建立在STUN协议之上的,它的协议头和body几乎是一样的,只是里面的一些属性和内容不一样,外壳形式什么的都是一样的,所以很多服务器都是将STUN协议和TURN协议放在一起形成了一个服务器,就是既提供STUN的功能又提供TURN的功能 。
TURN Client要求服务端分配一个公共IP和Port用于接收或发送数据
实际上在进行TURN协议的时候我们应该将它分成两类,一类是TURN Client一类是服务端,这与我们之前介绍的STUN是一样的,就是客户端服务器模式,那如何在这个TURN服务器上提供这种中继服务呢?那首先要TURN Client向TURN 服务端发送一个请求,发送了请求之后就就在服务端根据这个请求建立一个公共的IP地址和端口用户接收和发送数据 。那么对端(TURN client想要通讯的 对端)其实是不需要是一个TURN Client端的,它只需要正常的发送UDP包就可以了。
下面我们讲个具体的例子,上图是讲述整个TURN Client端去请求服务,服务端创建相应的公网IP地址和端口提供服务,以及与各个Peer终端进行交互的一个图,由上图我们可以看的有个TURN client端和一个TURN Server端以及两个Peer对端 ,首先我们来看看他们是怎么通讯的;
首先一个TURN Client端是在一个NAT之后的,这个时候TURN Client端它要发送一个请求给这个TURN Server,那么TURN Server是在另外一个网络地址,端口是3487,TURN Client在向TURN Server发送请求的时候会形成一组映射地址(出口)地址. 此时TURN Client发送一个Arrow的请求到TURN 服务端,TURN Server收到请求之后就会另外开通一个服务地址 192.0.2.15的地址端口是50000来提供这种UDP中转的这种服务,所以STURN对同一个 Client端来说有两个端口,一个是与TURN Client端连接的端口,另外一个是提供中转的端口50000,如果它现在与Peer B进行通讯,Peer B与TURN Server是在同一个网段,地址是192.0.2.210端口是49191,这个时候它就可以向中转的TURN Server中转的去发数据了。同样的他们建立连接之后 ,TURN Server也可以给这个Peer B发送数据,那这个时候Peer B如果发送数据到 50000这个端口,在TURN server的内部就会转到3478然后最终中继给这个TURN Client端;同样的如果TURN Client端想给Peer B发送消息的时候,它是先发到3478端口,然后经过内部的中转转成UDP然后再给Peer B,这就是它的一个逻辑。
那同样的在一个 NAT 之后的一个 Peer A也是可以通讯的,那么在通讯之前它首先要穿越NAT,在NAT上形成一个映射的IP地址也就是192.0.2.150端口是32102,所以对TURN Server来说它可以识别这个IP地址和端口就是这个地址,那如果与Peer B 进行通讯的时候,它就通过50000这个端口向这个32102端口发送消息,那么Peer A就收到了。相反的如果Peer A要给这个TURN Client端发送数据的时候,它就是往192.0.2.15:50000这个端口发数据,从这个端口又转到3478这个端口,最终传给TURN Client端。
这就是整个TURN中转的基本的例子。通过这个例子大家就很清楚它是怎么工作的。
TURN使用的传输协议
TURN Client 到TURN server使用的UDP、TCP包括加密后的TCP都是可以的,而对于TURN Server到Peer端,使用的都是UDP,所以这块大家一定要清楚;
TURN Allocate
那各个端如何让TURN Server提供通讯服务呢?它就要发送一个Allocate请求,在客户端首先发送一个Allocate请求到Server端,在Server首先是要做一些鉴权的处理,如果发现请求没有相应的权限,就返回一个401,就是无权限未授权的 ,整个底层用的都是STUN的消息格式,TURN Client收到这个未授权的消息之后它会重新再发一个请求这次会把鉴权信息也带过来给Server端,Server端这时候鉴权就通过了,然后返回一个成功应答 ,就是我给你提供的IP地址和端口是多少?这个时候Client就能和Peer A和Peer B进行通讯了,那在这之前通过Peer A和Peer B它要通过一个信令服务器那要拿到他们相应的地址,否则的话还是没法通信的;在这个前提之下如果中继服务已经开通了,TURN Client就可以源源不断的向TURN Server发送数据,然后TURN Server通过50000这个 端口给Peer A和Peer B进行转发;相反的如果Peer A 和Peer B给50000这个端口发数据,它就会被传给这个TURN 端;除此之外这个TURN Client端还要发送一个Refresh请求,实际上就是保活用的相当于心跳。在TURN Client端要求这个TURN Server分配 一个服务之后它是有一定的超时时间的,有可能是10分钟有可能是20分钟或者你设置为1个小时,那这个在这个设置时间段之内它是活着的,超过十分钟如果没有这个Refresh request刷新请求的话,那这个服务就算是失效了。那如果在失效之前它收到这个心跳,它就返回了一个成功,继续演示上面设置的一段时间,这就是它整体的一个分配和保活的一个逻辑。
TURN 发送机制
Send和Data
整个服务开通之后就涉及到对数据的发送了,那TURN有两种发送数据的机制,第一种就是Send和Data。
首先我们看Send和Data,要求服务端开启这个服务之后呢,紧接着就可以发送数据了,那在发送数据之前首先要鉴权,检查是否有发送的权限,这个完成之后它就来发数据,那么首先客户端向服务端发送数据,信令是Send,发送到服务端,服务端收到数据之后需要将TURN协议头给去掉,拿到里面的数据 ,里面的数据就是原始的UDP数据,拿到这个数据之后直接通过刚才的 5000端口转给你想转发的对端Peer A,那么数据就到Peer A,Peer A如果想给这个TURN Client发送数据,那它发的是UDP数据,那到了中继服务之后,中继服务STUN首先给它加一个消息头,加了消息头之后再通过这个TURN Client转给这个TURN Client端,所以这个Send和Data一个是表示上行一个是表示下型行。这就是TURN 的Send和Data的含义。
Channel
第二种是Channel。相比Channel的一个缺点就是在每次发送消息的时候都要带一个30多个字节的头,这个对于一般的情况下其实是不受影响的,但是因为我们传输的是流媒体数据,它的数据量非常大 ,如果每个数据包都带一个30多个的字节头那对整个带宽是有很大影响的。那如何去解决这个问题呢?
有了另外一种数据传输方式就是Channel,Channel方式就是大家规定一个Channel Id,我们都相当于加入了这个管道中,有了这个 管道我们就不用每次都带着这个头消息告诉你这是什么?一些基本信息在这里我们一开始就创建好了,我们都在管道里,我们现在只要发送数据就好了
在Channel模式下,首先客户端要发送一个ChannelBind绑定请求,服务端收到这个请求之后给它一个响应,绑定这个ChannelId就是这个16进制的0x4001,就是随便一个数它都能随机产生的,当然不能重复,那当整个Channel创建成功之后,Client端就可以发送数据给Server那这个Server就可以转发数据给这个Peer A,同样的如果Peer A如果想转发数据给这个TURN Server,那TURN Server再中继通过channel就转给这个Client端了。
那在这个时候另外一种方式,就是Send和Data这种方式其实是可以继续发送的,也就是说Send、Data和Channel这两种方式是可以并存的,可以混着发的,这就是Channel。
TURN的使用
TURN的使用,尤其是在WEBRTC中我们怎么使用:
1 首先是发送一个绑定,要进行这个打通,就是客户端到服务端要打通要拿到它的映射IP地址
2 之后发起方Caller要调用allocation,就是让TURN Server开辟一个服务接收我发送数据的IP地址和端口,就是一个中继的IP地址和端口,那整个服务开通之后,这个Caller获取了基本的信息,
3 然后通过信令将这个SDP也就是说一些媒体信息还有网络信息通过这个 SDP的offer发送该这个被调用者;
4 对方收到这个信息之后也要给这个TURN服务发送一个allocation,也要创建一个这样的服务,用来接收对方的数据,那这个创建成功之后,
5 因为这个Caller给它发了一个Offer然后它要回一个answer,这样他们整个数据交换就交换完了,
6 在交换的过程中实际就是将这个candidate,我们说SE的时候回说到这个candidate,它是一个候选者,相当于我每一个IP地址和端口都是一个候选者,就是有可能进行网络传输的一个地址,所以把它叫做candidate,就是将这个 candidate的这个地址进行交换,那它这个信息就交换了,
7 那交换完成之后通过这个ICE这个框架,首先检查P2P能否成功,因为最高的传输效率还是端对端之间,它不经过第三方的服务也不受第三方的服务带宽的影响,只要双方的带宽够就好了,这个是最基本的,所以它是最高效的,所以首先检查 P2P是否能够打通NAT,
8 如果打不通的话,这个时候就要通过中继,向对方所开通的端口去发送数据,这样另一方就能收到,同样的,另一方想要发送数据也给对方的中继服务发送数据就好了,那么以上就是整个TURN相关的一些基本的知识。
4 ICE
第一个是双方通信的Peer 有两台机子A和B ,他们都是在NAT之后 ,这里有两个NAT,在NAT外面有两个STUN 服务,其实也可以用一个STUN服务,他们主要用于终端去穿越NAT时候进行NAT穿越使用的。所以在这里会形成一个这个映射后的公网的IP地址。所以这两个可以是两个,也可以是一个,甚至可以是多个。那么还有一个呢,就是relay server,就是我们上节课所介绍的这个TURN Server。这个relay server的实际大多数情况下也具有STUN Server的功能,所以在这里呢,有可能这三个是合成一个的。那他也可以获取到这个NAT映射后的这个公网IP,但同时呢,它还有这个中继的功能。
那最后一个呢,是这个网络,整个这个信令是通过这个云端然后进行交互的,这就是他整个这张图包含了一些元素。那我们就来看看这ICE是如何进行工作并且使这个两个终端最终进行这个媒体流的通讯的啊,首先我们看左边,左边的时候,实际这个ICE干的第一件事儿是什么呢?就是让这个终端去得到所有能够连接到这个终端B的通路。那这个终端都有哪些通路呢?他首先他本机实际是有网卡的。这个本机的网卡就绑定了一个IP,那这个IP,实际就是有可能是一条通路了,因为如果他是在同一个局域网内,那么这俩就直接通过这个本地的IP地址就可以进行通讯。
所以这是本机的这个IP地址,这是一条通路,那么第二条通路呢,就是穿越NAT。当这个终端首先访问这个STUN服务,通过STUN服务呢,能获取到NAT映射的这个映射后的地址。他把这个地址去传给另外一个公关,那么这个终端拿到这个地址,并且把它穿越NAT就是向STUN服务请求拿到穿越后的这个映射地址交给这个终端A,那么双方都拿到他们的外网的IP地址之后呢,然后就可以进行这个尝试,P2P的穿越了。那如果穿越成功了,他们也直接就能通过NAT进行通讯了。那么如果不成功,那么还有地方,那么第三条路呢,就是走中继。那这个终端通过NAT将数据转给这个中继,那么中继呢,再向另外一个端去转发数据。那是不是就这三个呢?其实不是啊,在我这个终端上其实可能是多网卡。所以有可能是四个,但是可能有那个VPN那就有五个,有可能是虚拟IP那六个。那一个其实每一个都要做一些尝试,这个ICE的第一步就是将这个终端与另外一个终端的所有可能的通路全部收集起来,这是他做的第一个最主要的事儿。那将它所有的这个终端的这个通路收集起来之后干什么呢?然后他就传给对方,那对方也是做同样的这个操作,也收集所有的这个通路,然后呢,转给这个终端一。那双方都拿到这个对方的通路之后呢,然后进行尝试,怎么尝试呢?就是首先。让他这个主机的网卡的IP就去向这个主机的这个IP这个端口儿去发数据,但如果能通了,说明他是在同一个局域网内,但是直接通了,OK,如果不能通的情况下,那怎么办呢?他就尝试这个穿越NAT。就给他这边儿这个端口去发送数据,但肯定不通对吧,他是内网地址肯定不通,他不通了之后他就做另外一个尝试,那他使用什么呢,使用通过STUN服务拿到这个反射的地址,去这个地址进行尝试,看能不能通。那这个就根据这个我NAT类型儿,如果你是完全锥型儿的就可以通,如果限制型的端口型的就根据条件,那么,如果是对称型儿的两双方都是对称型儿的,就肯定不通,但如果做了这些尝试不通的情况下,那它自己会选择第三条路,就是通过NAT,然后将数据转发给这个relay Server,那么这里relay Server呢,再通过这个中转转给这个终端B,那最终呢,反正这三条路肯定要有一条路是通的,当然也可能不通,对吧,那如果这个relay server也是被这个防火墙给隔开了,那就不通,那这个时候呢,就双方就确实没法通信了,但一般情况下,经过relay,这是最后一条路的时候肯定会通的。
所以ICE的基本的功能就是第一步,要收集所有的通路,那么第二步就是对所有通路进行检测,看能不能通,那通了之后,那么ICE的这个工作就算结束了。 那么经过这张图呢,其实大家对这个ICE也基本上有了了解啊,那么下面我们就来看几个ICE的基本概念。
ICE Candidate
每个candidate是一个地址
那ICE的第一个基本概念是Candidate,也就是候选者,那什么是候选者呢?就是我刚才所说的就是他的一个通路,就是每个Candidate是一个地址,那么这些地址包括IP地址和端口。这是一个地址对。
例如:a=candidate:...UDP...192.169.1.2 typ host
你拿到这些通过之后,他们要交换对吧,那交换使用什么交换呢?是使用SDP,那么它是对于这个媒体信息以及网络信息的一个描述规范,那么这个规范呢,最终是通过信令将这个SDP发送给对方,双方拿到各自的对方的SDP,那么就能识别出对方都有哪些通路,有哪些通路。它的格式是什么呢?就是A代表一个属性呢,属性是一个Candidate,就是一个候选者一条通路。在这个这条通路里面必须包含一些基本信息,那第一个就是这个类型的,它是UDP的还是TCP的,他的IP地址是多少?端口是多少?类型是什么?是主机类型,就是我们自己本机的这个网卡还是这个反射后的,经过NAT反射后地址还是中继地址,他是有类型的。
那再下来呢,对这个有一个总结,就是每个候选者都包括了协议、IP、端口和类型,对,这就是candidate。那candidate类型其实我刚才我们已经介绍了,它有三种类型,那么第一种呢是主机候选者类型。那么第二个呢,是反射候选者类型,那么第三个是中继候选者类型,主机候选者就是我们网卡自己的这个IP地址还有端口,反射候选者类型就是经过NAT之后的这个IP地址和端口,中继就是通过TURN服务给开通的这个IP地址和端口
ICE具体做些什么
1 收集candidate
2 对candidate Pair排序
3 连通性检查
(1)收集Candidate
Host Candidate:本机所有IP和指定端口
Reflexive Candidate:STUN/TURN
Relay Candidate:TURN
什么是SDP
SDP(Session Description Protocol)它只是一种信息格式的描述标准,本身不属于传输协议,但是可以被其他传输协议用来交换必要的信息。
SDP是什么呢?它是一种这个信息格式的描述标准,就本身并不属于一种传输协议,但是呢,它可以被其他传输协议用来交换必要的信息,那这里包含的信息就包括了媒体信息和网络信息,这是最主要的。那我们看一个例子,
v=0是版本,一般都是0。
own 所有者
c表示connection表示连接这个网络的IPV4,
m:媒体信息,这次交换媒体信息里,媒体就是一个audio也就是音频,它使用的是RTP的协议,
a=rtpmap:0 PCMU/8000 对于这个音频它有一个参数,就是我使用的这个音频的编码方式是PCMU采样率是8000,
最重要的是最后两行,它检测到有两种Candidate就是候选的路,第一条是UDP的,IP 是 10.0.1.1端口是 8998,类型是host;第二种也是UDP的,IP是192.0.2.3端口是45664,类型是穿越NAT的映射地址,这里没有中继地址,就是最终不可能通过中继传输数据
(2)形成Candidate Pair
一方收集到所有候选者后,通过信令传给对方
同样,另一方收到候选者后,也做收集工作
当双方拿到全部列表后,将候选者形成匹配对儿
(3)连通性检查
对候选对进行优先级排序
对每个候选对进行发送检查
对每个候选对进行接收检查
检测连通性过程中,A首先向B发送的请求,但比如说B响应,你这是通的。B然后再发送请求,A在返回说OK,理论上需要四次,其实实际做了很多优化,都不需要这个完整的这个过程。
网络协议分析
常用工具
Linux服务端用tcpdump
其他端 WireShark
https://blog.youkuaiyun.com/holandstone/article/details/47026213 wire shark使用
WireShark中的逻辑运算
我先看一下WireShark包括哪些逻辑运算,这样我们就能进行各种复杂的组合了
与或非
与: and 或 &&
或: or 或 ||
非: not 或!
判断语句
等于:eq 或 ==
小于:lt 或 <
大于:gt 或 >
小于等于: le 或 <=
大于等于: ge 或 >=
不等于:ne 或 !=
这就是WireShark基本的判断语句,有了这些之后,加上WireShark的一些过滤方法可以组合成非常复杂的过滤条件了。
WireShark按协议过滤
stun
tcp
udp
我们打开WireShark这里包括所有的网卡
如果主界面找不到网卡,我们可以点击齿轮查看详情,这里可以看到每一个网卡,选中以后,点击开始监控
这样就能看到很多数据了,这样我们要按照协议去
这样我们要按照协议去抓取数据,比如说输入stun协议,这时候我们是没有任何stun数据的
大家可以打开下列网址
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
这个工具里会有一些ICE Server,大家可以点击下面的Gather candidates按钮升级所有的候选者,这时候他就会去向turn服务去发数据,这时候我本地的地址以及映射后的地址还有中继的地址都可以捕获到
其中host表示本机地址,
srflx表示映射的地址,映射后的地址表示本地地址出外网,映射后的地址是223.72.62.101,端口是15945,
relay表示中继地址,39.105.185.198表示在turn服务上开通的一个中继地址,端口是50447,
通过这几个我们就可以看的很清楚
这个时候我们通过网络抓取可以看到很多数据
首先我们看到一个Bingding Request,它的协议是STUN协议,点开我们可以看到Message Type就是0x0001就是Bing Request,
它有Length字段,Length是 0所以它的内容就没有,它只有一个包头,Message Cookie是2112a442,在RFC5389里面我们介绍过这个,还有一个Message Transaction ID表示事务ID,
这是请求
下面我们再看看响应
也就是说从192.168.1.6我本机,目的地址是39.105.180.98向这台机子发送了一个Bingding 请求,发送的这个事务ID是4666446f 4875796a344b4754,
这里同样的事务ID4666446f 4875796a344b4754给予了回复
这些回复的是0x0101,并且显示成功响应,长度是68,点开Attributes,
包括了映射地址
我们点开Attribute Type,这里面显示内容是TLV格式,T就是代表类型,后面的长度是8,代表的是一个IP地址和端口,
MAPPED-ADDRESS属性长度也是8, RESPONSE-ORIGIN属性也是8,一共长度就是24个字节
SOFTWARE是26
24加26就是50个字节,这个26是内容,前面两个是4个字节,四四十六,这每一个都是 16,最后还有个padding 2个字节,所以这样就组成了68个字节的包体长度。
所以大家这里就可以看到,我们此前说STUN协议的时候,发送的请求是 0x0001,响应的应该是0x0101。
我们的TURN协议是在STUN协议之上的,所以这里我们也会看到Allocate Request UDP,这是发送了一个分配服务的一个请求,我们记一下这里的事务ID
我们在看响应,是同一个事务ID表示同一个会话,这个Message Type回了一个 0x0113 是个错误,0x0001是绑定请求,0x0002表示加密信息,0x0003表示Allocate,这一次是失败了。
紧接着又发送了一次,这一次成功了
这次回复了一个0x0103,这是一个成功的消息,所以这次在TURN服务上,就把中转服务给开通了。
那么通过WireShark这种分析我就能将之前介绍的协议完全连在一起,说明我们的协议是没有任何问题的。
当然也可以抓取TCP协议或者UDP协议 ,直接在WireShark上面写就可以了。比如我们抓取一些udp
输入栏显示绿色表示抓取条件是对的,红色表示抓取条件是错误的
我们用的STUN协议、DNS都是UPD的,如果我们输入TCP,那些显示STUN的那些都没有了。
这是按协议过滤。
WireShark按IP过滤
ip.dst == 192.168.1.2
ip.dst表示过滤是目的ip,也就是发到192.168.1.2的
ip.src == 192.168.1.2
表示源ip,表示发送端是192.168.1.2
ip.addr == 192.168.1.2
ip.addr表示指定ip,不管是源ip还是目的ip,只要是这个ip地址就可以过滤掉
我们来尝试一下
这就是按ip过滤
WireShark按端口过滤
按照tcp,因为端口实际是定位到应用层了,就是ip层是没有端口的,那tcp是有端口的,udp也有端口
tcp.port == 8080
udp.port == 3478
udp.dtsport == 3478
udp.dtsport表示源端口
udp.srcport == 3478
udp.srcport表示目的端口
我们尝试下
WireShark按长度过滤
udp.length < 30
udp.length < 30
http.content_length < 30
那以上就是WireShark基本的用法。我们讲了与或非和判断语句,还有按协议、Ip、端口、长度、内容过滤,当然对内容来说就涉及到更高级的用法了。可以慢慢摸索。前面的这些只要掌握了,就可以做我们这边简单的网络数据分析了。
我们也可以写一个组合来进行过滤,比如说
与
非
大家可以按自己需要写各种个样的过滤条件
这样我们就可以做各种分析,后面我们会经常用到这个 WireShark去查一下协议的数据发送。