作者:Elias Zhang 聲網資深工程師,擁有從Iaas層的基礎資訊儲存服務到paas層的雲服務的職業經歷,喜歡python語言,習慣使用C#,熟悉基於和結合CDN的業務產品架構,點播、直播、雲導播等。喜歡探索問題和研究創新,擁有5項國家發明專利。
在直播是最常見的實時音視訊場景,而 RTMP 是該場景下最重要的協議之一,是很多初步接觸實時音視訊的開發者需要了解的。本文會一邊利用 winshark工具進行抓包,一邊從中分析 RTMP 協議的基本原理,幫助大家更容易地理解它。
先給出RTMP協議的原檔案 www.adobe.com/devnet/rtmp… 需要用到的時候可以參考一下~。
做推流直播接觸最多的並且最主要是RTMP協議
- RTMP協議是應用層協議,是要靠底層可靠的傳輸層(TCP)
- 協議(通常是TCP)來保證資訊傳輸的可靠性的。在基於傳輸層協議的連結建立完成後,RTMP協議也要客戶端和伺服器通過“握手”來建立基於傳輸層連結之上的RTMP Connection連結. 播放一個RTMP協議的流媒體需要經過以下幾個步驟:握手,建立網路連線,建立網路流,播放。伺服器和客戶端之間只能建立一個網路連線,但是基於該連線可以建立很多網路流。他們的關係如圖所示:
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/87d6f3a5ba40de55af006b03922c33601b0206c940753f53824247d2aa5c08d7.png)
- RTMP協議傳輸時會對資料做自己的格式化,這種格式的訊息我們稱之為RTMP Message,而實際傳輸的時候為了更好地實現多路複用、分包和資訊的公平性,傳送端會把Message劃分為帶有Message ID的Chunk,每個Chunk可能是一個單獨的Message,也可能是Message的一部分,在接受端會根據chunk中包含的data的長度,message id和message的長度把chunk還原成完整的Message,從而實現資訊的收發。 協議本身的詳細欄位和流程就不在這裡詳細解釋了,主要結合看包直觀的瞭解下這個協議的流程,更詳細的內容可以查閱前面給出的官方文件。
RTMP步驟:
1. 握手
要建立一個有效的RTMP Connection連結,首先要“握手”:客戶端要向伺服器傳送C0,C1,C2(按序)三個chunk,伺服器向客戶端傳送S0,S1,S2(按序)三個chunk,然後才能進行有效的資訊傳輸。RTMP協議本身並沒有規定這6個Message的具體傳輸順序,但RTMP協議的實現者需要保證這幾點:
- 客戶端要等收到S1之後才能傳送C2
- 客戶端要等收到S2之後才能傳送其他資訊(控制資訊和真實音視訊等資料)
- 服務端要等到收到C0之後傳送S1
- 服務端必須等到收到C1之後才能傳送S2
- 服務端必須等到收到C2之後才能傳送其他資訊(控制資訊和真實音視訊等資料) 握手開始於客戶端傳送C0、C1塊。伺服器收到C0或C1後傳送S0和S1。 當客戶端收齊S0和S1後,開始傳送C2。當伺服器收齊C0和C1後,開始傳送S2。 當客戶端和伺服器分別收到S2和C2後,握手完成。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/4726999eea267495037afa2c1b395551043b277056aa227edc0f6584a31f6413.png)
實際上真實發包如下:
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/3272ec0e325b51f0d1e179548133c81a09f206697726b37aa5ed995880195c98.png)
我們可以看見TCP的三次握手,RTMP基於TCP的可靠傳輸。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/13a1ec7af1e7ed9a4ff40ff653bdbe379fba14572ab7bf359badbef0dda37d3e.png)
接下去過濾rtmpt協議,rtmp的握手過程如下,我們發現真實發包是C0+C1一起發;S0,S1,S2一起發。
2. 建立網路連線(NetConnection)
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/6de94fff7971ac0dd3c2b58c8a505da98169dc985a6c97a7c391fceb6c33d968.png)
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/b4eb5a968315391b123f1d2c367a5e1c1466fa37e9fa1225a16831dd169dfb57.png)
a) 客戶端傳送命令訊息中的“連線”(connect)到伺服器,請求與一個服務應用例項建立連線。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/f8f7919b78539dde2e83d09c0c0851160ca606d64ec47b60ee10b67a4be2aa74.png)
開啟connect這個包,一個OSI5層協議模型,最後一個是RTMP協議傳送了connect連結訊息,檢視內容包含推流地址名,但是可以觀察到還沒有發流名,地址是有app名。
觀察一下RTMP的包頭
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/f49a22e18eab91b37e81abe2784eea9a2e1642be933d1ffe81546f2b4ed83231.png)
StreamID是每個訊息的唯一標識,劃分成Chunk和還原Chunk為Message的時候都是根據這個ID來辨識是否是同一個訊息的Chunk的,這裡面為0說明這個訊息是初始的0訊息。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/d6a1a8b2c3349d94b1abf2d1fb9e0117bcc674f58a83f6832201927a02584d92.png)
Chunk stream ID:message會拆分成多個chunk,同一個Chunk Stream ID必然屬於同一個Message。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/2133a903739736cd7ad6b1c7c1522a0901c3886b5ae26c68d49fa179e6025a77.png)
message type id(訊息的型別id):表示實際傳送的資料的型別,如8代表音訊資料、9代表視訊資料。
Format:指的是chunk type。共有4種不同的格式,其中第一種格式欄位為0,可以表示其他三種表示的所有資料,但由於其他三種格式是基於對之前chunk的差量化的表示,因此可以更簡潔地表示相同的資料,實際使用的時候還是應該採用儘量少的位元組表示相同意義的資料。因為type0是表示不同資料,其他是差量,所以可以想象如果搜不到type0的包說明這個流肯定有問題。可以通過“rtmpt.header.format == 0”過濾。
b) 伺服器接收到連線命令訊息後,傳送確認視窗大小(Window Acknowledgement Size)協議訊息到客戶端,同時連線到連線命令中提到的應用程式。
c) 伺服器傳送設定頻寬協議訊息到客戶端。
d) 客戶端處理設定頻寬協議訊息後,傳送確認視窗大小(Window Acknowledgement Size)協議訊息到伺服器端。
e) 伺服器傳送使用者控制訊息中的“流開始”(Stream Begin)訊息到客戶端。
f) 伺服器傳送命令訊息中的“結果”(_result),通知客戶端連線的狀態。 b~f如圖:在_result我們可以看到連結已經建立成功
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/5ba2e419d8bbb2cc3df57300716095db36c1f81cbb0d0f81af73433483ab0771.png)
接下去的包我們看到發了releaseStream命令,裡面的agora就是流名,所以一個推流地址我們可以抓包connect和releaseStream裡面拼接得出。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/2bf3b9492e08d44f4a0a17c0827cae59f358c1f0cfcc80aabc6c1dbcc789b8c0.png)
- 建立一個網路流(NetStream)
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/aa9daafc1ad213a08cec57a450c8147bec9dbb646ae43f4fef2e13ce79dec1eb.png)
提示:網路流代表了傳送多媒體資料的通道。伺服器和客戶端之間只能建立一個網路連線,且多個網路流可以複用這一個網路連線。
a. 客戶端向伺服器傳送請求建立流(createStream)。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/27b32bbde7d9cbc4560b2411e118e5da5fbe56be6887d1dbf096f47fc38a6fc0.png)
b. 伺服器收到請求後向客戶端傳送_result(),對建立流的訊息進行響應。此時NetStream建立完成。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/6bf94bb5bcf87b302ba9b7d6eb9eb1db1228e59f7f4ef9f95bce60e423523684.png)
4. PLAY 播放
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/2d51d4a9b6720ac54d4607a1eb626748c1d9a51332a6e3cd3a18d7ce435b97ac.png)
a) 客戶端傳送命令訊息中的“播放”(play)命令到伺服器。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/ae3278e86a76d76230b9c9bc8f18dedfe391612707a33cef88f692abf960dd92.png)
b) 接收到播放命令後,伺服器傳送設定塊大小(ChunkSize)協議訊息。
c) 伺服器傳送使用者控制訊息中的“streambegin”,告知客戶端流ID。
d) 播放命令成功的話,伺服器傳送命令訊息中的“響應狀態” NetStream.Play.Start & NetStream.Play.reset,告知客戶端“播放”命令執行成功。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/96ea59b4bb043289d49055b2cfeb4351210e3db2c80ed00b9f496f1ce256413d.png)
e) 在此之後伺服器傳送客戶端要播放的音訊和視訊資料。
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/f5d3e0290140aff471b5c45f52841ea57fce5884ebab5dfa8819b0f0f03df75b.png)
可以注意到裡面音訊type是8,視訊是9。
5. PUBLISH 推流
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/1bbe840ad9083dc7991e5faca577b075ecd1ba3c4c9fbe9e9acb2a4724d537a7.png)
和第四步play區別在於netstream的命令改為publish
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/a61d03d14265636ca1b59367a386342fe2c3fba6cd76a2f6530146fc50318505.png)
![通過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程](https://i.iter01.com/images/92eec82400de0b456b42b43186198a46c208e138b8d2f1072fb5aa349d888e99.png)
關於本文,如果你在跟隨步驟操作或閱讀時有任何疑問,請點選這裡跳轉至原文與作者直接交流。