介绍一些网络协议,主要内容来自极客时间趣谈网络协议.
直播中用到的协议
直播主要是视频,本质可以看作图片流。假设30帧的视频,像素1024*768,每个像素由RGB组成,每个8位,共24位。一秒钟的数据量30帧 × 1024 × 768 × 24 = 566,231,040Bits = 70,778,880Bytes达到4个G.
因此需要通过压缩或者说编解码减少数据传输,既然压缩,就要规定编解码协议
频流中的图片进行压缩,因为视频和图片有这样一些特点。
- 空间冗余:图像的相邻像素之间有较强的相关性,一张图片相邻像素往往是渐变的,不是突变的,没必要每个像素都完整地保存,可以隔几个保存一个,中间的用算法计算出来。
- 时间冗余:视频序列的相邻图像之间内容相似。一个视频中连续出现的图片也不是突变的,可以根据已有的图片进行预测和推断。
- 视觉冗余:人的视觉系统对某些细节不敏感,因此不会每一个细节都注意到,可以允许丢失一些数据。
- 编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多,类似霍夫曼编码(Huffman Coding)的思路。
视频编码流派
流媒体协议名称如下
- 名词系列一:AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。
- 名词系列二:H.261、 H.262、H.263、H.264、H.265。
名词系列三:MPEG-1、MPEG-2、MPEG-4、MPEG-7。
流派一:ITU(International Telecommunications Union)的VCEG(Video Coding Experts Group),这个称为国际电联下的VCEG。既然是电信,可想而知,他们最初做视频编码,主要侧重传输。名词系列二,就是这个组织制定的标准。
- 流派二:ISO(International Standards Organization)的MPEG(Moving Picture Experts Group),这个是ISO旗下的MPEG,本来是做视频存储的。例如,编码后保存在VCD和DVD中。当然后来也慢慢侧重视频传输了。名词系列三,就是这个组织制定的标准。
后来,ITU-T(国际电信联盟电信标准化部门,ITU Telecommunication Standardization Sector)与MPEG联合制定了H.264/MPEG-4 AVC
网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络包,从而得到里面的视频流,这个过程称为接流。
服务端接到视频流之后,可以对视频流进行一定的处理,例如转码,也即从一个编码格式,转成另一种格式。因为观众使用的客户端千差万别,要保证他们都能看到直播。
流处理完毕之后,就可以等待观众的客户端来请求这些视频流。观众的客户端请求的过程称为拉流。
如果有非常多的观众,同时看一个视频直播,那都从一个服务器上拉流,压力太大了,因而需要一个视频的分发网络,将视频预先加载到就近的边缘节点,这样大部分观众看的视频,是从边缘节点拉取的,就能降低服务器的压力。
所以整个直播流程,就可分为主播端采样编码推流,服务器接流处理分发,观众客户端拉流解码播放。

编码流程
视频序列分成三种帧。
- I帧,也称关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。
- P帧,前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。
- B帧,双向预测内插编码帧。B帧记录的是本帧与前后帧的差别。要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终的画面。
I 帧(Intra-coded picture,帧内编码帧)
- 别名:关键帧(Keyframe)。
- 特点:它是一张完整的、独立的图片,类似于一张高质量的 JPEG 照片。
- 压缩方式:只进行“帧内压缩”(只去除一张图片内部的冗余信息),不依赖任何其他画面。
- 作用:它是视频序列的“锚点”。当你在播放器里拖动进度条快进时,播放器必须先找到最近的 I 帧才能开始解码渲染。
P 帧(Predictive-coded picture,前向预测编码帧)
- 别名:差别帧。
- 特点:它不记录完整的图片,而只记录自己与前一个 I 帧(或前一个 P 帧)的“差异”和运动矢量。
- 压缩方式:帧间压缩(向前参考)。
- 作用:由于大多数视频相邻两帧的画面变化很小(比如只有人在动,背景没动),P 帧通过只存储变化的部分,数据量通常只有 I 帧的 1/2 到 1/4,大大节省了空间。
B 帧(Bidirectionally predictive-coded picture,双向预测编码帧)
- 别名:双向参考帧。
- 特点:它是压缩率最高的帧。它不仅参考前面已经出现的画面,还要参考后面即将出现的画面,通过两头对比,计算出自己的画面。
- 压缩方式:帧间压缩(双向参考)。
- 作用:B 帧只记录极少的运动变化信息,数据量极小,通常只有 I 帧的 1/10 左右。
I帧最完整,B帧压缩率最高,而压缩后帧的序列,应该是在IBBP的间隔出现的。这就是通过时序进行编码。

在一帧中,分成多个片,每个片中分成多个宏块,每个宏块分成多个子块,这样将一张大的图分解成一个个小块,可以方便进行空间上的编码。尽管时空非常立体地组成了一个序列,但是总归还是要压缩成一个二进制流。这个流是有结构的,是一个个的网络提取层单元(NALU,Network Abstraction Layer Unit)。变成这种格式就是为了传输,因为网络上的传输,默认的是一个个的包,因而这里也就分成了一个个的单元。
每一个NALU首先是一个起始标识符,用于标识NALU之间的间隔;然后是NALU的头,里面主要配置了NALU的类型;最终Payload里面是NALU承载的数据。
在NALU头里面,主要的内容是类型NAL Type。
- 0x07表示SPS,是序列参数集, 包括一个图像序列的所有信息,如图像尺寸、视频格式等。
- 0x08表示PPS,是图像参数集,包括一个图像的所有分片的所有相关信息,包括图像类型、序列号等

在传输视频流之前,必须要传输这两类参数,不然无法解码。为了保证容错性,每一个I帧前面,都会传一遍这两个参数集合。
如果NALU Header里面的表示类型是SPS或者PPS,则Payload中就是真正的参数集的内容。
如果类型是帧,则Payload中才是正的视频数据,当然也是一帧一帧存放的,前面说了,一帧的内容还是挺多的,因而每一个NALU里面保存的是一片。对于每一片,到底是I帧,还是P帧,还是B帧,在片结构里面也有个Header,这里面有个类型,然后是片的内容。
这样,整个格式就出来了,一个视频,可以拆分成一系列的帧,每一帧拆分成一系列的片,每一片都放在一个NALU里面,NALU之间都是通过特殊的起始标识符分隔,在每一个I帧的第一片前面,要插入单独保存SPS和PPS的NALU,最终形成一个长长的NALU序列。
推流流程
使用RTMP协议推送网络包。RTMP是基于TCP的,因而肯定需要双方建立一个TCP的连接。在有TCP的连接的基础上,还需要建立一个RTMP的连接,也即在程序里面,你需要调用RTMP类库的Connect函数,显示创建一个连接。
RTMP也需要先简历连接,协商本版号和时间戳等信息。
客户端发送C0、C1、 C2,服务器发送S0、 S1、 S2。
首先,客户端发送C0表示自己的版本号,不必等对方的回复,然后发送C1表示自己的时间戳。
服务器只有在收到C0的时候,才能返回S0,表明自己的版本号,如果版本不匹配,可以断开连接。
服务器发送完S0后,也不用等什么,就直接发送自己的时间戳S1。客户端收到S1的时候,发一个知道了对方时间戳的ACK C2。同理服务器收到C1的时候,发一个知道了对方时间戳的ACK S2。于是,握手完成。
握手之后,双方需要互相传递一些控制信息,例如Chunk块的大小、窗口大小等。
真正传输数据的时候,还是需要创建一个流Stream,然后通过这个Stream来推流publish。推流的过程,就是将NALU放在Message里面发送,这个也称为RTMP Packet包。Message的格式就像这样。

发送的时候,去掉NALU的起始标识符。RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须在一个Chunk发送完成之后,才能开始发送下一个Chunk。每个Chunk中都带有Message ID,表示属于哪个Message,接收端也会按照这个ID将Chunk组装成Message。
连接的时候,设置的Chunk块大小就是指这个Chunk。将大的消息变为小的块再发送,可以在低带宽的情况下,减少网络拥塞。
假设一个视频的消息长度为307,但是Chunk大小约定为128,于是会拆分为三个Chunk。
第一个Chunk的Type=0,表示Chunk头是完整的;头里面Timestamp为1000,总长度Length 为307,类型为9,是个视频,Stream ID为12346,正文部分承担128个字节的Data。
第二个Chunk也要发送128个字节,Chunk头由于和第一个Chunk一样,因此采用Chunk Type=3,表示头一样就不再发送了。
第三个Chunk要发送的Data的长度为307-128-128=51个字节,还是采用Type=3。

拉流流程
看直播的观众就可以通过RTMP协议从流媒体服务器上拉取,但是这么多的用户量,都去同一个地方拉取,服务器压力会很大,而且用户分布在全国甚至全球,如果都去统一的一个地方下载,也会时延比较长,需要有分发网络。
分发网络分为中心和边缘两层。边缘层服务器部署在全国各地及横跨各大运营商里,和用户距离很近。中心层是流媒体服务集群,负责内容的转发。智能负载均衡系统,根据用户的地理位置信息,就近选择边缘服务器,为用户提供推/拉流服务。中心层也负责转码服务,例如,把RTMP协议的码流转换为HLS码流。
类似地,客户端拉流被,客户端通过RTMP协议拉取,然后组合为NALU,解码成视频格式进行播放。
P2P协议
无论是HTTP的方式,还是FTP的方式,都有一个比较大的缺点,就是难以解决单一服务器的带宽压力, 因为它们使用的都是传统的客户端服务器的方式。
后来,一种创新的、称为P2P的方式流行起来。P2P就是peer-to-peer。资源开始并不集中地存储在某些设备上,而是分散地存储在多台设备上。这些设备我们姑且称为peer。
想要下载一个文件的时候,你只要得到那些已经存在了文件的peer,并和这些peer之间,建立点对点的连接,而不需要到中心服务器上,就可以就近下载文件。一旦下载了文件,你也就成为peer中的一员,你旁边的那些机器,也可能会选择从你这里下载文件,所以当你使用P2P软件的时候,例如BitTorrent,往往能够看到,既有下载流量,也有上传的流量,也即你自己也加入了这个P2P的网络,自己从别人那里下载,同时也提供给其他人下载。可以想象,这种方式,参与的人越多,下载速度越快。
通过.torrent文件,也就是种子,知道文件信息。.torrent文件由两部分组成,分别是:announce(tracker URL)和文件信息。
文件信息里面有这些内容。
- info区:这里指定的是该种子有几个文件、文件有多长、目录结构,以及目录和文件的名字。
- Name字段:指定顶层目录名字。
- 每个段的大小:BitTorrent(简称BT)协议把一个文件分成很多个小段,然后分段下载。
- 段哈希值:将整个种子中,每个段的SHA-1哈希值拼在一起。
下载时,BT客户端首先解析.torrent文件,得到tracker地址,然后连接tracker服务器。tracker服务器回应下载者的请求,将其他下载者(包括发布者)的IP提供给下载者。下载者再连接其他下载者,根据.torrent文件,两者分别对方告知自己已经有的块,然后交换对方没有的数据。此时不需要其他服务器参与,并分散了单个线路上的数据流量,因此减轻了服务器的负担。
下载者每得到一个块,需要算出下载块的Hash验证码,并与.torrent文件中的对比。如果一样,则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容的准确性问题。
这种方式特别依赖tracker。tracker需要收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。虽然下载的过程是非中心化的,但是加入这个P2P网络的时候,都需要借助tracker中心服务器,这个服务器是用来登记有哪些用户在请求哪些资源。
所以,这种工作方式有一个弊端,一旦tracker服务器出现故障或者线路遭到屏蔽,BT工具就无法正常工作了。
DHT去中心化网络
后来就有了一种叫作DHT(Distributed Hash Table)的去中心化网络。每个加入这个DHT网络的人,都要负责存储这个网络里的资源信息和其他成员的联系信息,相当于所有人一起构成了一个庞大的分布式存储数据库。
有一种著名的DHT协议,叫Kademlia协议。
任何一个BitTorrent启动之后,它都有两个角色。一个是peer,监听一个TCP端口,用来上传和下载文件,这个角色表明,我这里有某个文件。另一个角色DHT node,监听一个UDP的端口,通过这个角色,这个节点加入了一个DHT的网络。在DHT网络里面,每一个DHT node都有一个ID。这个ID是一个很长的串。每个DHT node都有责任掌握一些知识,也就是文件索引,也即它应该知道某些文件是保存在哪些节点上。它只需要有这些知识就可以了,而它自己本身不一定就是保存这个文件的节点。
数据中心
数据中心里面是服务器。服务器被放在一个个叫作机架(Rack)的架子上面。数据中心的入口和出口也是路由器,由于在数据中心的边界,就像在一个国家的边境,称为边界路由器(Border Router)。为了高可用,边界路由器会有多个。
一般家里只会连接一个运营商的网络,而为了高可用,为了当一个运营商出问题的时候,还可以通过另外一个运营商来提供服务,所以数据中心的边界路由器会连接多个运营商网络。
数据中心里面往往有非常多的机器,当塞满一机架的时候,需要有交换机将这些服务器连接起来,可以互相通信。这些交换机往往是放在机架顶端的,所以经常称为TOR(Top Of Rack)交换机。
当一个机架放不下的时候,就需要多个机架,还需要有交换机将多个机架连接在一起。这些交换机对性能的要求更高,带宽也更大。这些交换机称为汇聚层交换机(Aggregation Layer)。

数据中心里面的每一个连接都是需要考虑高可用的。这里首先要考虑的是,如果一台机器只有一个网卡,上面连着一个网线,接入到TOR交换机上。如果网卡坏了,或者不小心网线掉了,机器就上不去了。所以,需要至少两个网卡、两个网线插到TOR交换机上,但是两个网卡要工作得像一张网卡一样,这就是常说的网卡绑定(bond)。
这就需要服务器和交换机都支持一种协议LACP(Link Aggregation Control Protocol)。它们互相通信,将多个网卡聚合称为一个网卡,多个网线聚合成一个网线,在网线之间可以进行负载均衡,也可以为了高可用作准备。
网卡有了高可用保证,但交换机还有问题。如果一个机架只有一个交换机,它挂了,那整个机架都不能上网了。因而TOR交换机也需要高可用,同理接入层和汇聚层的连接也需要高可用性,也不能单线连着
最传统的方法是,部署两个接入交换机、两个汇聚交换机。服务器和两个接入交换机都连接,接入交换机和两个汇聚都连接,当然这样会形成环,所以需要启用STP协议,去除环,但是这样两个汇聚就只能一主一备了。

另一种方法是,将多个交换机形成一个逻辑的交换机,服务器通过多根线分配连到多个接入层交换机上,而接入层交换机多根线分别连接到多个交换机上,并且通过堆叠的私有协议,形成双活的连接方式。

汇聚层将大量的计算节点相互连接在一起,形成一个集群。在这个集群里面,服务器之间通过二层互通,这个区域常称为一个POD(Point Of Delivery),有时候也称为一个可用区(Available Zone)。
当节点数目再多的时候,一个可用区放不下,需要将多个可用区连在一起,连接多个可用区的交换机称为核心交换机。
VPN
有的公司有多个数据中心,需要将多个数据中心连接起来,或者需要办公室和数据中心连接起来。这该怎么办呢?
- 第一种方式是走公网,但是公网太不安全,你的隐私可能会被别人偷窥。
- 第二种方式是租用专线的方式把它们连起来,这是土豪的做法,需要花很多钱。
- 第三种方式是用VPN来连接,这种方法比较折中,安全又不贵。
VPN,全名Virtual Private Network,虚拟专用网,就是利用开放的公众网络,建立专用数据传输通道,将远程的分支机构、移动办公人员等连接起来。

VPN工作方式
VPN通过隧道技术在公众网络上仿真一条点到点的专线,是通过利用一种协议来传输另外一种协议的技术,这里面涉及三种协议:乘客协议、隧道协议和承载协议。
IPsec VPN。这是基于IP协议的安全隧道协议,为了保证在公网上面信息的安全,因而采取了一定的机制保证安全性。
- 机制一:私密性,防止信息泄露给未经授权的个人,通过加密把数据从明文变成无法读懂的密文,从而确保数据的私密性。 前面讲HTTPS的时候,说过加密可以分为对称加密和非对称加密。对称加密速度快一些。而VPN一旦建立,需要传输大量数据,因而我们采取对称加密。但是同样,对称加密还是存在加密密钥如何传输的问题,这里需要用到因特网密钥交换(IKE,Internet Key Exchange)协议。
- 机制二:完整性,数据没有被非法篡改,通过对数据进行hash运算,产生类似于指纹的数据摘要,以保证数据的完整性。
- 机制三:真实性,数据确实是由特定的对端发出,通过身份认证可以保证数据的真实性。

有了IPsec VPN之后,客户端发送的明文的IP包,都会被加上ESP头和IP头,在公网上传输,由于加密,可以保证不被窃取,到了对端后,去掉ESP的头,进行解密。
