2)详探
应用程序app
app是application的缩写,代表客户端要链接到的,rtmp服务器的应用程序,这个一般我们在nginx服务器的配置选项中可以看到。对于该object,首先使用app来表示此object表示的名称,之后按照AMF0格式来表示具体的值。此处app的值为rtmp_live,是字符串类型,占用9个字节。正好,此次请求的rtmp的应用程序确实是rtmp_live,可参照rtmp配置文件。
版本flashVer
flashVer表示flash播放器的版本号,同样首先通过一个字符串来表示object的类型;然后再用AMF0的编码格式对具体的值进行编码,此处是flash的版本号,采用字符串表示,值为"LNX 9,0,124,2"。
链接tcUrl
服务器的URL地址,其格式为
protocol://servername:port/appName/appInstance
比如例子中服务器的url地址为
rtmp://192.17.1.200:1935/rtmp_live(此处省略了appInstance)
看抓包文件,我们可以看出,对于tcUrl字段的object,同样是先用一个字符串来表示类型,其后以AMF0的编码格式对具体的数值进行编码(此处还是字符串形式)。
代理标志fpad
fpad是一个布尔值,用来表示是否使用代理。如果使用,值为true,否则值为false。抓包文件中还是首先用一个字符串表示Object的类型,然后按AMF0的格式进行编码。此处为Bool类型,所以比较简单,0x01表示布尔类型,Bool类型没有长度字段,占用一个字节,其后使用一个字节来表示true或者false,此处为false。
能力capabilities
同样,先用字符串表示object的类型,然后再用跟着具体的值,此处为一个数值类型,值为15,即capabilities的值为15。
音频audioCodecs
audioCodes表示支持的音频编码格式,支持的每一种格式用一个bit位来表示,所有的比特位进行或运算得到最终的值。以本抓包文件为例,codecs的值为4071,4071对应的二进制为 0000 1111 1110 0111。
然后我们再看下不同的bit位对应的codec编码器,如下图:
对照表,我们可以得出本次连接支持哪些格式的音频编码。简单举个例子,4071中包含0x0400,表明此次rtmp请求音频是支持AAC的,其他标志类似。
视频videoCodecs
说完audioCodecs,再说videoCodecs就简单多了,同样的逻辑,同样的codec表示方式 。此抓包文件中videoCodec的值为252,对应的二进制表示为:
252 = 0x0040 | 0x0080 | 0x0010 | 0x0020 | 0x0040 | 0x0080
再对照videoCodecs的定义
以0x0080为例,我们得知此次rtmp连接针对视频支持H264编码。
帧搜索videoFunction
videoFunction也比较简单,先用字符串表明类型,然后紧跟着一个number类型的数据,值为1。videoFunction的值为1,表示客户端可以执行精确到帧的搜索。
3)connect消息流
客户端和服务端通过connect进行交互的一些流程:
- 客户端发送connect命令到服务器,请求与服务端的application进行连接;
- 服务端收到connect命令后,服务器会发送协议消息“Window Acknowledgement size”消息到客户端。服务端同时连接到connect中请求的application;
- 服务端发送协议消息“Set Peer BandWidth”到客户端;
- 客户端在处理完服务端发来的“Set Peer BandWidth”消息后,向服务端发送“Window Acknowledgment”消息;
- 服务端向客户端发送一条用户控制消息(Stream Begin);
- 如果连接成功,服务端向客户端发送_result消息,否则发送_error消息。
至此,关于connect的消息暂时到这,接下来我们分别看看“Window Acknowledgement Size”、“Set Peer BandWidtth”以及_result消息。
5.result消息
rtmp客户端发送connect消息之后,rtmp server会给客户端发送_result消息,通过该消息通知客户端连接状态(success/fail)。
1)概览
一个_result消息由4部分组成,类型标识,transaction ID,properties,response related information,这四部分均以AMF格式进行编码。
2)详解
典型的类型+长度+值的AMF组合,不多说了,_result表示消息类型。
transaction ID
transcationID,按照AMF0格式编码,0x00表示数字格式,其后用8个字节表示ID,对于connect消息的回复,此ID恒为1。
properties
可以看出properties中包含了两个Object类型的数据,一个fmsVer表示了FMS 服务器的版本信息(此处为FMS/3,0,1,123),另外一个capabilites表示容量,值为31。关于RTMP Body中的Object对象数据组织格式,就不赘述了,可以参照之前对于connect消息的详细解释。
response related information
通过看抓包文件,我们可以看出这里包含许多关于connect连接的响应,以object类型进行组织。有level,此处为status,意味表示连接的状态;code,表示代码,可以理解为某种编号,此处为NetConnection.Connect.Succes;description可以理解为对code的描述,此处为‘Connection succeeded’。code与description联合起来,表示此次连接成功;还有关于objectEncoding字段,表示object以那种方式编码(AMF0或者AMF3),此处值为0,表示采用AMF0编码。
客户端收到_result,获取连接状态为Connection succeeded。这样关于连接的建立基本就完成了,接下来就可以进行流相关的操作了。
6.connect后续三剑客
connect消息的时候,我们说过服务器收到connect消息之后,会向客户端发送Window Acknowledgement Size消息和Set Peer Bandwidth消息。
1)概览
示例中服务器ip地址是192.17.1.200,客户端ip地址是192.17.1.92,客户端向服务器发送connect消息之后,服务器向客户端发送了Window Acknowledgement Size和Set Peer Bandwidth消息。
2)Window Acknowledgement Size消息
Window Acknowledgement Size用来通知对端,如果收到该大小字节的数据,需要回复一个Acknowledgement消息,也就是ACK。本例中,设置的大小为500000,也就是服务端通知客户端如果收到了50000字节的数据,需要向服务端发送一个ACK的消息,而实际上一般情况一个会话中能达到如此大的数据量比较少,所以我们也看到会回复ACK消息的消息比较少,更多的只是看到设置接收窗口大小的消息(Window Acknowledgement Size),接下来我们看一下抓包。
消息格式比较简单,组织结构是RTMP Header + RTMP Body,Header的结构就不赘述了,参考前面的文章;Body中直接使用4个字节表示要设置的大小,此处为0x004c4b40=5000000。
3)Acknowlegement消息
Acknowlegement消息可以理解为Window Acknowlegement Size满足条件的触发消息,当一端收到的数据大小满足Window Acknowledgement Size设置的大小时,向对端发送Ack消息。
Acknowlegement消息,也按照RTMP Header + RTMP Body进行组织,其Body也直接使用4个字节,表示收到数据满足Window Acknowledgement Size的最后一个数据包的序列号。我们来看一个抓包文件:
该条消息表示,在序列号为2507670的时候,rtmp客户端已累计收到5000000个字节的数据,此时向服务端发送一个ACK。
另外我们也验证一下,交互过程中,更多的看到Window Acknowledgement Size消息,而很少看到Acknowlegement消息。
说明:wireshark中可以针对rtmp消息进行过滤
过滤Window Acknowledgement Size
rtmpt.header.typeid == 0x05
过滤Acknowledgement
rtmpt.header.typeid == 0x03
照猫画虎,可以对照下表进行过滤:
更多的关于wireshark抓包关于rtmp的过滤条件,参照:
https://www.wireshark.org/docs/dfref/r/rtmpt.html
4)Set Peer BandWidth消息
该消息里设置对端输出带宽,对端是通过设置Window Acknowledgement Size来实现流量控制的。超过Window Acknowledgement Size后未确认(不发送Acknowledgement)发送端将不再发送消息。所以对端收到set peer bandwidth后,如果之前发送的Window Acknowledgement Size和这里写的的Window Acknowledgement Size不一样,一般会发送一个Window Acknowledgement Size。
刚开始建立连接,服务器向客户端发送Set Peer Bandwidth消息,客户端第一次收到Set Peer Bandwidth消息,之前没有发送过Window Acknowledgement Size,所以在这里向服务端发送一次消息。
Set Peer Bandwidth消息还是按照RTMP Header + RTMP Body的格式组成。RTMP Body由两个字段组成,一个是Window acknowledgement size,占用4个字节;一个是limit type,表示限制的类型,可取的值为0(Hard),1(soft), 2(Dynamic)。本例中采用的是Dynamic。
limity type表示了不同的限制策略:
- Hard:收到消息的一端需要按照消息中设置的Window size进行限制;
- Soft:收到消息的一端按照消息中设置的Window size或者已经生效的限制进行限制,以两者中较小的为准。
- Dynamic:如果之前的类型为Hard,则此消息也为Hard类型,否则忽略该类型。