精华内容
下载资源
问答
  • 浏览器协议栈(左图传统HTTP 右图WebRTC) RTP/SRTP RTP是未加密的数据,SRTP是加密后的数据。 RTP协议 对于同一帧进行分包/组包处理的时候,seq number是连续的,timestamp是同一时间戳,H264内部有...

    浏览器协议栈(左图传统HTTP 右图WebRTC)

     

     

    RTP/SRTP

    RTP是未加密的数据,SRTP是加密后的数据。

     

    RTP协议

    对于同一帧进行分包/组包处理的时候,seq number是连续的,timestamp是同一时间戳,H264内部有起始位和结束位。

    个人总结的RTP封装

    rtp.h

    /*
     * 作者:一笑奈何
     * 博客:cn-yixiaonaihe.blog.csdn.net
     */
    
    #ifndef _RTP_H_
    #define _RTP_H_
    #include <stdint.h>
    
    #define RTP_VESION              2
    
    #define RTP_PAYLOAD_TYPE_H264   96
    #define RTP_PAYLOAD_TYPE_AAC    97
    
    #define RTP_HEADER_SIZE         12
    #define RTP_MAX_PKT_SIZE        1400
    
    
    
    
     /*
      *
      *    0                   1                   2                   3
      *    7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0
      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      *   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      *   |                           timestamp                           |
      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      *   |           synchronization source (SSRC) identifier            |
      *   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
      *   |            contributing source (CSRC) identifiers             |
      *   :                             ....                              :
      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      *
      */
    struct RtpHeader
    {
    	/* byte 0 */
    	uint8_t csrcLen : 4;  //指示CSRC标识符个数 
    	uint8_t extension : 1; //如果X = 1,则在RTP报头后跟有一个扩展报头
    	uint8_t padding : 1;  //如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分
    	uint8_t version : 2;  //当前协议版本号为2
    
    	/* byte 1 */
    	uint8_t payloadType : 7;//用于说明RTP报文中有效载荷的类型
    	uint8_t marker : 1;//不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始
    
    	/* bytes 2,3 */
    	uint16_t seq;//用于标识发送者所发送的RTP报文的序列号
    
    	/* bytes 4-7 */
    	uint32_t timestamp;//必须使用90kHZ时钟频率(程序中的90000)
    
    	/* bytes 8-11 */
    	uint32_t ssrc;//用于标识同步信源
    };
    
    struct RtpPacket
    {
    	struct RtpHeader rtpHeader;
    	uint8_t payload[0];
    };
    
    void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,
    	uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,
    	uint16_t seq, uint32_t timestamp, uint32_t ssrc);
    int rtpSendPacket(int socket, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize);
    
    #endif //_RTP_H_
    

    rtp.c

    /*
     * 作者:一笑奈何
     * 博客:cn-yixiaonaihe.blog.csdn.net
     */
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    
    #include "rtp.h"
    
    void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,
    	uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,
    	uint16_t seq, uint32_t timestamp, uint32_t ssrc)
    {
    	rtpPacket->rtpHeader.csrcLen = csrcLen;
    	rtpPacket->rtpHeader.extension = extension;
    	rtpPacket->rtpHeader.padding = padding;
    	rtpPacket->rtpHeader.version = version;
    	rtpPacket->rtpHeader.payloadType = payloadType;
    	rtpPacket->rtpHeader.marker = marker;
    	rtpPacket->rtpHeader.seq = seq;
    	rtpPacket->rtpHeader.timestamp = timestamp;
    	rtpPacket->rtpHeader.ssrc = ssrc;
    }
    /*
     * 函数功能:发送RTP包
     * 参数 socket:表示本机的udp套接字
     * 参数 ip:表示目的ip地址
     * 参数 port:表示目的的端口号
     * 参数 rtpPacket:表示rtp包
     * 参数 dataSize:表示rtp包中载荷的大小
     * 放回值:发送字节数
     */
    
    int rtpSendPacket(int socket, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
    {
    	struct sockaddr_in addr;
    	int ret;
    
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(port);
    	addr.sin_addr.s_addr = inet_addr(ip);
    
    	rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);
    	rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
    	rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);
    
    	ret = sendto(socket, (const char*)rtpPacket, dataSize + RTP_HEADER_SIZE, 0,
    		(struct sockaddr*)&addr, sizeof(addr));
    
    	rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
    	rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
    	rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);
    
    	return ret;
    }
    

    RTCP/SRTCP

    RTCP用来控制RTP的传输,出现丢包,抖动,延迟等通过RTCP上报并且计算。

     

    RTP端口多为偶数

    在WebRTC需要进行NAT穿越,如果是多个窗口的话。RTP和RTCP可能会复用窗口

    一个RTCP中会携带多个报告数据

     

    200 发送者报告(作为发送和接收同时发送)   201 接收者报告(加入的发送者的详细信息)   202  对于源的描述    203   关闭共享源  204使用自定义子类型

    192 WebRTC中  FIR 解码时出错时,向源请求一个关键帧(防出错);

    193  发送NACK 包重传

    FB分为三种:

    传输层反馈包;编解码器反馈包;应用层反馈包

    205  RTPFB  rtp报文丢失重传 属于传输层反馈包

    206  PSFB    指定净荷重传,数据编解码器反馈包,指定净荷重传里面又分如下三种:
    1、PLI    (Picture Loss Indication) 视频帧丢失重传。
    2、SLI    (Slice Loss Indication)    slice丢失重转。
    3、RPSI (Reference Picture Selection Indication)参考帧丢失重传

     

    RTCP Sender Report  发送报告

     

    Sender Information block  发送者自己的发送块

     

    Receiver Report block  接收者发送报告

     

     

    RTCP Receiver Report  接收报告

     

    RTCP SR/RR 发送时机

     

    RTCP SR

    RTCP RR

    RTCP SDES

     

    SDES item

     

    RTCP BYE

    RTCP APP

    DTLS

    加密前进行整数检测和加密算法的协商。

    RTPFB

     

    PSFB

     

    展开全文
  • 7-webrtc传输基本知识

    2021-01-05 08:09:29
    WebRTC传输基本知识 WebRTC首先要解决的是两个浏览器之间如何实现音视频的实时互动,对于底层来说就是如何实现两个端点之间高效的网络传输,为了解决这个问题WebRTC引入了很多的传输协议 NAT (Network Address ...

    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.csdn.net/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去查一下协议的数据发送。

    展开全文
  • DTLS (Datagram Transport Layer Security) 基于 UDP 场景下数据包可能丢失或重新排序的现实情况下,为 UDP 定制和改进的 TLS 协议。在 WebRTC 中使用 DTLS 的地方包括两部分:协商和管理 SRTP 密钥和为 DataChannel...

    作者|进学

    审校|泰一

    DTLS (Datagram Transport Layer Security) 基于 UDP 场景下数据包可能丢失或重新排序的现实情况下,为 UDP 定制和改进的 TLS 协议。在 WebRTC 中使用 DTLS 的地方包括两部分:协商和管理 SRTP 密钥和为 DataChannel 提供加密通道。

    本文结合实际数据包分析 WebRTC 使用 DTLS 进行 SRTP 密钥协商的流程。并对在实际项目中使用 DTLS 遇到的问题进行总结。

    DTLS 协议简介

    在分析 DTLS 在 WebRTC 中的应用之前,先介绍下 DTLS 协议的基本原理。DTLS 协议由两层组成: Record 协议 和 Handshake 协议

    1. Record 协议:使用对称密钥对传输数据进行加密,并使用 HMAC 对数据进行完整性校验,实现了数据的安全传输。
    2. Handshake 协议:使用非对称加密算法,完成 Record 协议使用的对称密钥的协商。

    HandShake

    TLS 握手协议流程如下,参考 RFC5246

    DTLS 握手协议流程如下,参考 RFC6347


    TLS 和 DTLS 的握手过程基本上是一致的,差别以及特别说明如下:

    • DTLS 中 HelloVerifyRequest 是为防止 DoS 攻击增加的消息。
    • TLS 没有发送 CertificateRequest,这个也不是必须的,是反向验证即服务器验证客户端。
    • DTLS 的 RecordLayer 新增了 SequenceNumber 和 Epoch,以及 ClientHello 中新增了 Cookie,以及 Handshake 中新增了 Fragment 信息(防止超过 UDP 的 MTU),都是为了适应 UDP 的丢包以及容易被攻击做的改进。参考 RFC 6347
    • DTLS 最后的 Alert 是将客户端的 Encrypted Alert 消息,解密之后直接响应给客户端的,实际上 Server 应该回应加密的消息,这里我们的服务器回应明文是为了解析客户端加密的那个 Alert 包是什么。

    RecordLayer 协议是和 DTLS 传输相关的协议,UDP 之上是 RecordLayer,RecordLayer 之上是 Handshake 或 ChangeCipherSpec 或 ApplicationData。RecordLayer 协议定义参考 RFC4347,实际上有三种 RecordLayer 的包:

    • DTLSPlaintext,DTLS 明文的 RecordLayer。
    • DTLSCompressed,压缩的数据,一般不用。
    • DTLSCiphertext,加密的数据,在 ChangeCipherSpec 之后就是这种了。

    没有明确的字段说明是哪种消息,不过可以根据上下文以及内容判断。比如 ChangeCipherSpec 是可以通过类型,它肯定是一个 Plaintext。除了 Finished 的其他握手,一般都是 Plaintext。

    SRTP 密钥协商

    角色协商

    在 DTLS 协议,通信的双方有 ClientServer 之分。在 WebRTC 中 DTLS 协商的身份是在 SDP 中描述的。描述如下,参考 SDP-Anatomy 中 DTLS 参数

    a=setup:active
    

    setup 属性在 RFC4145

    setup:active,作为 client,主动发起协商

    setup:passive, 作为 sever,等待发起协商

    setup:actpass, 作为 client,主动发起协商。作为 server,等待发起协商。

    算法协商 - Hello 消息

    ClienHello 和 ServerHello 协商 DTLS 的 Version、CipherSuites、Random、以及 Extensions。

    • Version:Client 给出自己能支持的、或者要使用的最高版本,比如 DTLS1.2。Server 收到这个信息后,根据自己能支持的、或者要使用的版本回应,比如 DTLS1.0。最终以协商的版本也就是 DTLS1.0 为准。
    • CipherSuites:Client 给出自己能支持的加密套件 CipherSuites,Server 收到后选择自己能支持的回应一个,比如 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014),加密套件确定了证书的类型、密钥生成算法、摘要算法等。
    • Random:双方的随机数,参与到生成 MasterSecret。MasterSecret 会用来生成主密钥,导出 SRTP 密钥。详见 [导出 SRTP 密钥]
    • Extensions:Client 给出自己要使用的扩展协议,Server 可以回应自己支持的。比如 Client 虽然设置了 SessionTicket TLS 这个扩展,但是 Server 没有回应,所以最终并不会使用这个扩展。

    Cipher Suite

    在 Hello 消息中加密套接字使用 IANA 中的注册的名字。IANA 名字由 Protocol,Key Exchange Algorithm,Authentication Algorithm,Encryption Algorithm ,Hash Algorithm 的描述组成。例如,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 的含义如下:

    • Protocol: Transport Layer Security (TLS)
    • Key Exchange: Elliptic Curve Diffie-Hellman Ephemeral (ECDHE)
    • Authentication: Rivest Shamir Adleman algorithm (RSA)
    • Encryption: Advanced Encryption Standard with 128bit key in Galois/Counter mode (AES 128 GCM)
    • Hash: Secure Hash Algorithm 256 (SHA256)

    加密套接字在 ciphersuite.info 可以查到。在查到 IANA 名字的同时,也可以查到在 OpenSSL 中的名字。TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 在 OpenSSL 中的名字为 ECDHE-RSA-AES128-GCM-SHA256
    Note: 关于 Authentication (认证)、KeyExchange (密钥交换)、Encryption (加密)、MAC (Message Authentication Code) 消息摘要等,可以参考 RSA 密钥协商

    Extension

    DTLS 的扩展协议,是在 ClientHello 和 ServerHello 的 Extensions 信息中指定的,所有的 TLS 扩展参考 TLS Extensions。下面列出几个 WebRTC 用到的扩展:

    • use_srtp: DTLS 握手完成后 (Finished),使用 SRTP 传输数据,DTLS 生成 SRTP 的密钥。 RFC5764。ClientHello 中的扩展信息定义了 RFC5764 4.1.2. SRTP Protection Profilessrtp_mki
    • supported_groups,原来的名字为 elliptic_curves,描述支持的 ECC 加密算法,参考 RFC8422 5.1.1.Supported Elliptic Curves Extension,一般用的是 secp256r1。
    • signature_algorithms,DTLS1.2 的扩展,指定使用的 Hash 和 Signature 算法,参考 RFC5246 7.4.1.4.1. Signature Algorithms。DTLS1.0,RSA 用的是 md5sha1 摘要算法,DSA 用的是 sha1 摘要算法。
    • extended_master_secret,扩展 MasterSecret 的生成方式,参考 RFC7627。在 KeyExchange 中,会加入一些常量来生成 MasterSecret。TLS 定义了扩展方式,如果用这个扩展,DTLS 的方式和 TLS 会有些不同。
    • renegotiation_info, 参考 RFC5746

    除了这些扩展协议,和 SRTP 密钥导出相关的还有:

    RFC5705: Keying Material Exporters for Transport Layer Security (TLS),DTLS 如何从 MasterSecret 导出 Key,比如 SRTP 的 Key。
    RFC5764: DTLS Extension to Establish Keys for the SRTP,DTLS 的 use_srtp 扩展的详细规范,包括 ClientHello 扩展定义、Profile 定义、Key 的计算。

    身份验证 - Certificate


    数字证书是由一些公认可信的证书颁发机构签发的,不易伪造。数字证书可以用于接收者验证对端的身份,接收者收到某个对端的证书时,会对签名颁发机构的数字签名进行检查,一般来说,接收者事先就会预先安装很多常用的签名颁发机构的证书(含有公开密钥),利用预先的公开密钥可以对签名进行验证。

    Server 端通过 Hello 消息,协商交换密钥的方法后,将 Server 证书发送给 Client,用于 Client 对 Server 的身份进行校验。Server 发送的证书必须适用于协商的 KeyExchange 使用的加密套接字,以及 Hello 消息扩展中描述的 Hash/Signature 算法对。

    在 WebRTC 中,通信的双方通常将无法获得由知名根证书颁发机构 (CA) 签名的身份验证证书,自签名证书通常是唯一的选择。RFC4572 定义一种机制,通过在 SDP 中增加自签名证书的安全哈希,称为 “证书指纹”,在保证 SDP 安全传输的前提下,如果提供的证书的指纹与 SDP 中的指纹匹配,则可以信任自签名证书。在实际的应用场景中,SDP 在安全的信令通道 (https) 完成交换的,SDP 的安全完整是可以做到的。这样在 DTLS 协商过程中,可以使用证书的指纹,完成通信双方的身份校验。证书指纹在 SDP 中的描述如下,参考 SDP-Anatomy 中 DTLS 参数

    a=fingerprint:sha-256 49:66:12:17:0D:1C:91:AE:57:4C:C6:36:DD:D5:97:D2:7D:62:C9:9A:7F:B9:A3:F4:70:03:E7:43:91:73:23:5E
    

    密钥交换 - KeyExchange

    ServerKeyExchange 用来将 Server 端使用的公钥,发送给 Client 端。分为两种情况:

    1. RSA 算法:如果服务端使用的是 RSA 算法,可以不发送这个消息,因为 RSA 算法使用的公钥已经在 Certificate 中描述。
    2. DH 算法,是根据对方的公钥和自己私钥计算共享密钥。因为 Client 和 Server 都只知道自己的私钥,和对方的公钥;而他们的私钥都不同,根据特殊的数学特性,他们能计算出同样的共享密钥。关于 DH 算法如何计算出共享密钥,参考 DH 算法

    ClientKeyExchange 用来将 Client 使用的公钥,发送给 Server 端。

    1. RSA 算法:如果密钥协商使用的 RSA 算法,发送使用 server 端 RSA 公钥,对 premaster secret 加密发送给 server 端。
    2. DH 算法:如果密钥协商使用的 DH 算法,并且在证书中没有描述,在将客户端使用的 DH 算法公钥发送给 Server 端,以便计算出共享密钥。
      KeyExchange 的结果是,Client 和 Server 获取到了 RSA Key, 或通过 DH 算法计算出共享密钥。详见 [导出 SRTP 密钥] 的过程

    证书验证 - CertificateVerify

    使用 ClientRequest 中描述的 Hash/Signature 算法,对收到和发送的 HandShake 消息签名发送个 Server。Server 端对签名进行校验。

    加密验证 - Finished

    当 Server 和 Client 完成对称密钥的交换后,通过 ChangeCipherSpec 通知对端进入加密阶段,epoch 加 1。

    随后 Client 使用交换的密钥,对 “client finished” 加密,使用 Finished 消息,发送给服务端。Server 使用交换的密钥,对 “server finished” 进行加密发送给客户端。一旦验证了 finished 消息后,就可以正常通信了。

    导出 SRTP 密钥

    上面介绍了 DTLS 的过程,以下通过结合上面例子给出的实际数据,详细说明 SRTP 密钥的导出步骤。

    协商后的加密算法

    加密套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
    椭圆曲线算法为:secp256r1,椭圆曲线的点压缩算法为:uncompressed。
    椭圆曲线算法的基础知识的介绍在 ECC 椭圆曲线加密算法 - ECDH,ECDHE,ECDSA ,由文档中我们可以知道,确定椭圆曲线加密算法有如下参数:

    • 素数 p,用于确定有限域的范围
    • 椭圆曲线方程中的 a,b 参数
    • 用于生成子群的的基点 G
    • 子群的阶 n
    • 子群的辅助因子 h
      定义为六元组(p,a,b,G,n,h)

    通过在 SECG-SEC2 2.4.2 Recommended Parameters secp256r1 中可以查到 secp256r1 对应的参数如下:

    secp256r1使用的参数如下:
    使用的素数p:
    p=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
    =2224(232−1)+2192+296−1
    椭圆曲线E:y^2=x^3+ax+b的参数定义如下:
    a=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
    b=5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
    基点G的非压缩格式:
    G=046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
    有限域的阶n:
    n=FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
    辅助因子h:
    h=01
    

    通过 KeyExchange 交换椭圆曲线算法公钥

    ECDH Server Parameter

    Pubkey:04b0ce3c5f2c4a9fbe7c2257c1328438f3378f74e9f528b6e27a00b44eee4c19e5e6b2cb6cab09f796bcf8c05102b2a4bcdc753d91cc4f431f558c845a1ba6f1ce
    

    记为 Spk

    ECDH Client Paramter

    PubKey: 0454e8fbef1503109d619c39be0ccaf89efa3c3962300476465cbc66b15152cd8a900c45d506420f0123e65d8fbb70cb60b497893f81c5c2a0ef2f4bc2da996d9e
    

    记为 Cpk

    根据 ECDHE 算法计算共享密钥 S(pre-master-secret)

    假设 Server 的使用的椭圆曲线私钥为 Ds, Client 使用的 Dc,计算共享密钥的如下,参考 ECC 椭圆曲线加密算法 - ECDH,ECDHE,ECDSA

    S = Ds * Cpk = Dc * Spk

    这个共享密钥 S 就是我们在 RFC 文档中看到的 pre-master-secret

    计算 master secret

    计算 master secret 过程如下,可参考 Computing the Master Secret

        master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)[0..47];
    

    计算出来的 master_secret 为 48 Bytes,其中 ClientHello.randomServerHello.random 在 Hello 消息中给出。PRF 是伪随机数函数 (pseudorandom function),在协商的加密套件中给出。

    使用 master_secrete 导出 SRTP 加密参数字节序列

    使用 RFC5705 4. Exporter Definition 给出的计算方式,使用参数 master_secretclient_randomserver_random 计算字节序列:

    key_block = PRF(master_secret, "EXTRACTOR-dtls_srtp", client_random + server_random)[length]
    

    DTLS-SRTP 4.2. Key Derivation 中描述了需要的字节序列长度。

    2 * (SRTPSecurityParams.master_key_len + SRTPSecurityParams.master_salt_len) bytes of data
    

    master_key_len 和 master_salt_len 的值,在 user_srtp 描述的 profile 中定义。我们的实例中使用的 profile 为 SRTP_AES128_CM_HMAC_SHA1_80,对应的 profile 配置为:

    SRTP_AES128_CM_HMAC_SHA1_80
            cipher: AES_128_CM
            cipher_key_length: 128
            cipher_salt_length: 112
            maximum_lifetime: 2^31
            auth_function: HMAC-SHA1
            auth_key_length: 160
            auth_tag_length: 80
    

    也就是我们需要 (128/8+112/8)*2 = 60 bytes 字节序列。

    导出 SRTP 密钥

    计算出 SRTP 加密参数字节序列,在 DTLS-SRTP 4.2. Key Derivation 描述了字节序列的含义:

    client_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; // 128 bits
    server_write_SRTP_master_key[SRTPSecurityParams.master_key_len]; // 128 bits
    client_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; // 112 bits
    server_write_SRTP_master_salt[SRTPSecurityParams.master_salt_len]; // 112 bits
    

    至此我们得到了,Client 和 Server 使用的 SRTP 加密参数:master_key 和 master_salt.

    DTLS 超时重传

    DTLS 是基于 UDP 的,不可避免会出现丢包,需要重传。如果处理不当,会导致整个通信双方无法建立会话,通话失败。RFC6347 4.2.4 给出了超时和重传机制。
    在处理重传时,以下几点需要注意:

    1. 在 DTLS 协议中,为了解决丢包和重传问题,新增了 message_seq. 在发送 DTLS 重传消息时,一定要更新其中的 message_seq,这样对端将把包识别是一个重传包,响应正确消息。否则,会默默丢弃这些包,不进行响应。
    2. 当 server 端收到 client 的 FINISHED 消息,并发送 FINISHED 消息给 client,更新 server 状态为协商完成,开始发送 SRTP 数据。此时发送给 client 的 FINISHED 消息,出现丢包。client 收到 SRTP 数据后丢弃。同时,再次发送 FINISHED 消息到 server,server 要正确响应。否则,会导致 DTLS 协商完成的假象,通话失败。
    3. 使用 openssl 1.1.1 之前版本,无法设置 DTLS 超时重传时间,可以超时重传机制不可用,大家开始转向使用 boringssl。openssl 1.1.1 开始版本已经支持设置 DTLS 超时重传,达到和 boringssl 同样的效果。参考 DTLS_set_timer_cb

    OpenSSL 的 DTLS 功能

    DTLS 是一个庞大的协议体系,其中包括了各种加密,签名,证书,压缩等多种算法。大多数项目是基于 OpenSSL 或 BoringSSL 实现的 DTLS 功能。在实际项目使用 OpenSSL 的 DTLS 功能,与协商有关的接口总结如下。

    1. X509_digest,计算证书 fingerprint,用在 SDP 协商中的 fingerprint 属性。
    2. SSL_CTX_set_cipher_list,设置使用的加密套件,通过设置算法的描述,影响 Hello 消息中的 cipher list。
    3. SSL_CTX_set1_sigalgs_list 设置签名算法。通过设置签名算法的描述,影响 hello 消息中 signature_algorithms 扩展。signature_algorithms 对 DTLS 的 Hello 消息KeyExchangeCerficateVerify 消息。signature_algorithms 设置不正确,会出现 internal error,不容易定位问题。
    4. SSL_CTX_set_verify 设置是否校验对端的证书。由于在 RTC 中大多数据情况下使用自签证书,所以对证书的校验,已校验身份是需要的。
    5. SSL_CTX_set_tlsext_use_srtp 设置 srtp 扩展。srtp 扩展中的 profile,影响 srtp 加密时使用密钥的协商和提取。
    6. SSL_set_options 使用 SSL_OP_NO_QUERY_MTU 和 [SSL_set_mtu] 设置 fragment 的大小。默认 OpenSSL 使用 fragment 较小。通过上面两个接口,设置适合网络情况的 fragment。
    7. DTLS_set_timer_cb,设置超时重传的 Callback,由 callback 设置更合理的超时重传时间。

    在开源项目 SRS 中已经支持了 WebRTC 的基础协议,对 DTLS 协议感兴趣的同学,可以基于 SRS 快速搭建本机环境,通过调试,进一步加深对 DTLS 的理解。

    总结

    本文通过 WebRTC 中 SRTP 密钥的协商过程,来说明 DTLS 在 WebRTC 中的应用。DTLS 协议设计的各个加密算法的知识较多,加上 TLS 消息的在各种应用场景中的扩展,难免有理解和认知不到的地方,还需要进一步深入探索。

    参考文献

    1. TLS 1.2
    2. DTLS 1.2
    3. TLS Session Hash Extension
    4. TCP-Based Media Transport in the Session Description Protocol
    5. TLS Extension
    6. SRTP Extension for DTLS
    7. OpenSSL Man
    8. ECC 椭圆曲线加密算法 - 介绍
    9. ECC 椭圆曲线加密算法 - 有限域和离散对数
    10. ECC 椭圆曲线加密算法 - ECDH、ECDHE 和 ECDSA

    「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云技术交流群,和作者一起探讨音视频技术,获取更多行业最新信息。

    展开全文
  • 点击上方蓝字关注我们对网络协议来说,需要做的通常就两件事情:1、建立连接,2、传输数据,WebRTC也不例外。假设WebRTC应用的两端已经建立了连接,那么,剩下就是如何传输数据的问题了。WebRTC同时支持传输音视频...
    07ab06068fdbf7f54c9936637f09db83.png

    点击上方蓝字关注我们

    对网络协议来说,需要做的通常就两件事情:1、建立连接,2、传输数据,WebRTC也不例外。

    假设WebRTC应用的两端已经建立了连接,那么,剩下就是如何传输数据的问题了。

    WebRTC同时支持传输音视频数据、自定义应用数据。这其中,涉及多种协议,包括UDP、RTP/SRTP、RTCP/SRTCP、DTLS、SCTP。

    这些协议名字比较相似,很容易让人混淆,简单总结下:

    1. 传输音视频数据相关协议:UDP、DTLS、RTP/SRTCP;

    2. 传输自定义应用数据相关协议:UDP、DTLS、SCTP;

    下面就简单介绍下,这些协议是做什么的,有什么区别,存在什么联系。

    加密信道建立:UDP、DTLS

    对WebRTC应用来说,不管是音视频数据,还是自定义应用数据,都要求基于加密的信道进行传输。DTLS 有点类似 TLS,在UDP的基础上,实现信道的加密。

    DTLS的主要用途,就是让通信双方协商密钥,用来对数据进行加解密。

    1. 通信双方:通过DTLS握手,协商生成一对密钥;

    2. 发送方:对数据进行加密;

    3. 发送方:通过UDP传输加密数据;

    4. 接收方:对加密数据进行解密;

    音视频数据传输:RTP/SRTP、RTCP/SRTCP

    首先,我们先来看下RTP、RTCP的大概用途:

    1. RTP(Realtime Transport Protocol):实时传输协议,主要用来传输对实时性要求比较高的数据,比如音视频数据。

    2. RTCP(RTP Trasport Control Protocol):RTP传输控制协议,跟RTP在同一份RFC中定义,主要用来监控数据传输的质量,并给予数据发送方反馈。

    也就是说:

    1. RTP用来传输音视频数据;

    2. RTCP用来传输(质量)控制数据;比如监控传输的质量,并在会话双方之间进行同步,方便WebRTC根据传输质量进行动态调整,比如传输的速率、视频的码率等。

    至于SRTP、SRTCP,分别在RTP、RTCP的基础上加了个S(Secure),表示安全的意思,这个就是DTLS做的事情了。

    结合前面内容,总结一下音视频数据的发送过程:

    1. 通信双方:通过DTLS握手,协商生成一对密钥;

    2. 数据发送方:将音视频数据封装成RTP包,将控制数据封装成RTCP包;

    3. 数据发送方:利用加密密钥,对RTP包、RTCP包进行加密,生成SRTP包、SRTCP包;

    4. 数据发送方:通过UDP传输SRTP包、SRTCP包;

    备注:SRTP/SRTCP包中,除了加密数据,还有其他信息,这里不展开细节。

    自定义应用数据传输:SCTP

    SCTP(Stream Control Transmission Protocol):流控制传输协议。

    之前介绍过,RTP/RTCP主要用来传输音视频,是为了流媒体设计的。而对于自定义应用数据的传输,WebRTC中使用了SCTP协议。

    同样的,SCTP依赖DTLS建立的加密信道,对于自定义应用数据的发送,流程如下:

    1. 通信双方:通过DTLS握手,协商生成一对密钥;

    2. 数据发送方:将自定义应用数据,通过密钥进行加密,生成SCTP包;

    3. 数据发送方:通过UDP传输SCTP包;

    写在后面

    为了便于讲解,跳过了很多协议的细节,有些地方可能会不够严谨,感兴趣的同学可以进行进一步研究,比如以下问题:

    1. 传输层用了UDP,UDP本身是不可靠的,那么,音视频数据、自定义用户数据的时序、质量是如何保证的?

    2. RTP用来传递音视频数据,为什么还需要有RTCP?

    3. 为什么说RTP不适合传输自定义用户数据?

    4. SCTP如何从协议层面兼顾传输的效率和质量?如何实现自定义数据的高效传递?

    5. 其他

    相关链接

    RTP: A Transport Protocol for Real-Time Applications
    https://tools.ietf.org/html/rfc3550

    Stream Control Transmission Protocol
    https://tools.ietf.org/html/rfc4960

    Datagram Transport Layer Security
    https://tools.ietf.org/html/rfc4347

    github博客:

    https://github.com/chyingp/blog
    新浪微博:

    http://weibo.com/chyingp
    站酷主页:

    http://www.zcool.com.cn/u/346408/

    写在最后

    方凳雅集是由阿里巴巴B系6大BU(1688,ICBU,零售通,AE,企业金融,考拉)共同维护的公众号奥,我们会定期发送优质好文,欢迎扫码关注

    bff454604120e3fcc5c997676b9e5be5.png

    求关注

    求转发

    9fbfe5ad6435d0e6d6de1ccd699783b6.png6a8d219ab9633807507f008bb4d0286b.png
    展开全文
  • 对网络协议来说,需要做的通常就两件事情:1、建立连接,2、传输数据,WebRTC也不例外。 假设WebRTC应用的两端已经建立了连接,那么,剩下就是如何传输数据的问题了。 WebRTC同时支持传输音视频数据、自定义应用数据...
  • SDP(Session Description Protocol)描述会话协议,它只是一种信息格式的描述标准,本身不属于传输协议,但是可以被其他传输协议用来交换必要的信息,用于两个会话实体之间的媒体协商。 2、sdp协议结构 SDP的文本信息...
  • 哈喽,大家好,我又来了。 前段时间在学WebRtc,刚开始在想这个玩意该如何学习呢? 1,先从接口学起呢?...咱们先从传输数据通信协议学起吧,WebRtc采用得是UDP->RTP/RTCP作为传输模块的一部分,负责对发送端采
  • WebRTC中的协议

    2020-02-02 21:09:56
    1 前言 掐指一算,貌似快两年没写博客了。最近在看有关音频视频直播相关的东西,其中就包括WebRTC。...开门见山的说,WebRTC使用UDP作为传输协议。使用TCP作为传输协议的话,如果中间出现丢包的情况,由于TCP需...
  • webRTC协议分析

    千次阅读 2018-04-04 11:35:44
    http Hyper Text Transport Protocol 超文本传输协议 应用层协议 WebSocket 包括 SIP WebSocket 和 XMPP WebSocket 子协议 STUN NAT 会话穿透实用工具 回话建立之前会发送stun报文进行测试 确定是否位于NAT...
  • 今天我们来介绍WebRTC传输相关的基本知识,那么在介绍这些基础知识之前 ,我们首先要清楚使用WebRTC的目的以及解决的根本问题是什么? 那么WebRTC首先要解决的是两个浏览器之间如何实现音视频的实时互动,对于底层...
  • WebRTC之SDP协议

    2021-04-09 09:48:23
    SDP(Session Description Protocol) 是一种会话描述协议,基于文本,其本身并不属于传输协议,需要依赖其它的传输协议(比如 SIP 和 HTTP)来交换必要的媒体信息,用于两个会话实体之间的媒体协商。 下面这个链接...
  • webrtc 和 rtp 协议

    2021-02-08 12:11:47
    rtp协议 rtp协议为实时传输协议
  • WebRTC协议栈视图结构

    万次阅读 2017-09-04 19:33:58
    WebRTC协议栈视图结构作者 completion 原理: 在Basic-WebTRC中可知,数据的传输层分两种方式: (1) srtp/rtp方式,这种传输主要针对流媒体数据。 (2)datachannel方式(SCTP协议),这种传输任何的数据类型,它不Care...
  • WebRTC项目中,RTP/RTCP模块作为传输模块的一部分,负责对发送端采集到的媒体数据进行进行封包,然后交给上层网络模块发送;在接收端RTP/RTCP模块收到上层模块的数据包后,进行解包操作,最后把负载发送到解码模块...
  • 大多数人都认为webrtc更适合现代视频传输需求的发展,未来webrtc的直播方案将会取代rtmp的直播方案,其实不然,RTMP由于具备良好的实用性和推流的稳定性并没有被市场淘汰。我们在开发出了webrtc协议的播放后,仍然对...
  • 流媒体传输中常用的RTCP包主要有SR/RR/SDES/BYE/APP/XR,主要由RFC 3611和RFC 3550定义。而WEBRTC视频中常用的RTCP包比语音要多很多,语音的RTCP主要用于状态传递,统计数据。而视频中的RTCP更多赋予了控制功能...
  • webrtc-rtp协议分析

    2020-12-10 20:30:54
    填充可能用于某些具有固定长度的加密算法,或者用于在底层数据单元中传输多个RTP包。 由于Opus的包大小差不太多,但又不完全一样,所以会出现很多大小差不多却又不一样的小包,这对于服务器GSO发送这些包会带来很大...
  • WebRTC 中的 SDP 协议

    千次阅读 2017-07-05 13:50:44
    SDP纯粹是会话描述的格式,它不包含传输协议WebRTC 会话就是由 SDP 描述的。对于 WebRTC 来说,最重要的是 SDP 中的媒体和传输信息。媒体信息包括:媒体类型(视频,音频) 传输协议(RTP/UDP/IP,H.320等) 媒体...
  • SDP(Session Description Protocol)会话描述协议,是一种会话描述格式,不是传输协议(例如RTP,RSTP是传输协议),SDP协议是也是基于文本的协议。SDP很早就用于流媒体中来描述媒体信息,例如RTSP的SDP文件,现在...
  • 这本质是一种重传,但是跟传输层的重传的区别是,它重传是最新生成的帧。 PLI 是Picture Loss Indication,SLI 是Slice Loss Indication。 发送方接收到接收方反馈的PLI或SLI需要重新让编码器生成关键帧并发送给...
  • WebRTC主要在连接建立阶段用到SDP,连接双方通过信令服务交换会话信息,包括音视频编解码器(codec)、主机候选地址、网络传输协议等。 下面先简单介绍下SDP的格式、常用属性,然后通过WebRTC连接建立过程生...
  • TURN介绍 目的是解决对称NAT无法穿越的问题,... 建立在STUN之上,消息格式使用STUN格式消息,协议头和body基本一样,外壳形式一样内容有所不同 TRUN Client要去服务端分配一个公共IP和Port用于接收或发送数据 ...
  • WebRTC通话原理-SDP协议

    2021-03-23 11:28:15
    所以在数据传输时两端都能够理解彼此的数据。本质上,这些描述内容的元数据并不是媒体流本身。 从技术上讲,SDP并不是一个真正的协议,而是一种数据格式,用于描述在设备之间共享媒体的连接。SDP包含内容非常多,如...
  • WEBRTC RTP/RTCP协议

    2018-01-29 15:39:31
    RF3550定义实时传输协议RTP和它的控制协议RTCP。RTP协议是Internet上针对流媒体传输的基础协议,该协议详细说明在互联网上传输音视频的标准数据包格式。RTP本身只保证实时数据的传输,并不能提供可靠传输、流量控制...
  • Webrtc (一):协议

    2021-04-01 18:51:26
    WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。...
  • 会议 基于WebRTC的会议媒体服务器。 执照 源代码是根据条款提供。
  • 如果让你从0开发一套实时互动直播系统,你首先要选择网络传输协议。UDP 还是 TCP?答案是:UDP。为什么实时传输不能用 TCP ?TCP 的目的就是实现数据的可靠传输,因此他有一套 握手,发送 -> 确认,超时 -> ...
  • WEBRTC

    2015-10-14 13:59:00
    rtsp的优势非常不明显 rtmp倒是短时间内很明显hls做回放,http做直播好多做IPTV的 基本都是这个套路音频编码,视频编码,传输协议,会话协议 开源项目WebRTC,IAX,SIP了解下WEBRTC,IAX2,RTP,H264,ILBC ...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 223
精华内容 89
关键字:

webrtc传输协议