經過 wireshark 抓包瞭解直播流媒體 RTMP 協議基本過程

做者:Elias Zhang 聲網資深工程師,擁有從Iaas層的基礎信息存儲服務到paas層的雲服務的職業經歷,喜歡python語言,習慣使用C#,熟悉基於和結合CDN的業務產品架構,點播、直播、雲導播等。喜歡探索問題和研究創新,擁有5項國家發明專利。html

在直播是最多見的實時音視頻場景,而 RTMP 是該場景下最重要的協議之一,是不少初步接觸實時音視頻的開發者須要瞭解的。本文會一邊利用 winshark工具進行抓包,一邊從中分析 RTMP 協議的基本原理,幫助你們更容易地理解它。python

先給出RTMP協議的原文件 www.adobe.com/devnet/rtmp… 須要用到的時候能夠參考一下~。服務器

作推流直播接觸最多的而且最主要是RTMP協議網絡

  • RTMP協議是應用層協議,是要靠底層可靠的傳輸層(TCP)
  • 協議(一般是TCP)來保證信息傳輸的可靠性的。在基於傳輸層協議的連接創建完成後,RTMP協議也要客戶端和服務器經過「握手」來創建基於傳輸層連接之上的RTMP Connection連接. 播放一個RTMP協議的流媒體須要通過如下幾個步驟:握手,創建網絡鏈接,創建網絡流,播放。服務器和客戶端之間只能創建一個網絡鏈接,可是基於該鏈接能夠建立不少網絡流。他們的關係如圖所示:

  • RTMP協議傳輸時會對數據作本身的格式化,這種格式的消息咱們稱之爲RTMP Message,而實際傳輸的時候爲了更好地實現多路複用、分包和信息的公平性,發送端會把Message劃分爲帶有Message ID的Chunk,每一個Chunk多是一個單獨的Message,也多是Message的一部分,在接受端會根據chunk中包含的data的長度,message id和message的長度把chunk還原成完整的Message,從而實現信息的收發。 協議自己的詳細字段和流程就不在這裏詳細解釋了,主要結合看包直觀的瞭解下這個協議的流程,更詳細的內容能夠查閱前面給出的官方文檔。

RTMP步驟:架構

1. 握手app

要創建一個有效的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後,握手完成。

實際上真實發包以下:3d

咱們能夠看見TCP的三次握手,RTMP基於TCP的可靠傳輸。orm

接下去過濾rtmpt協議,rtmp的握手過程以下,咱們發現真實發包是C0+C1一塊兒發;S0,S1,S2一塊兒發。cdn

2. 創建網絡鏈接(NetConnection)

a) 客戶端發送命令消息中的「鏈接」(connect)到服務器,請求與一個服務應用實例創建鏈接。

打開connect這個包,一個OSI5層協議模型,最後一個是RTMP協議發送了connect連接消息,查看內容包含推流地址名,可是能夠觀察到尚未發流名,地址是有app名。

觀察一下RTMP的包頭

StreamID是每一個消息的惟一標識,劃分紅Chunk和還原Chunk爲Message的時候都是根據這個ID來辨識是不是同一個消息的Chunk的,這裏面爲0說明這個消息是初始的0消息。

Chunk stream ID:message會拆分紅多個chunk,同一個Chunk Stream ID必然屬於同一個Message。

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咱們能夠看到連接已經創建成功

接下去的包咱們看到發了releaseStream命令,裏面的agora就是流名,因此一個推流地址咱們能夠抓包connect和releaseStream裏面拼接得出。

  1. 創建一個網絡流(NetStream)

提示:網絡流表明了發送多媒體數據的通道。服務器和客戶端之間只能創建一個網絡鏈接,且多個網絡流能夠複用這一個網絡鏈接。

a. 客戶端向服務器發送請求建立流(createStream)。

b. 服務器收到請求後向客戶端發送_result(),對建立流的消息進行響應。此時NetStream建立完成。

4. PLAY 播放

a) 客戶端發送命令消息中的「播放」(play)命令到服務器。

b) 接收到播放命令後,服務器發送設置塊大小(ChunkSize)協議消息。

c) 服務器發送用戶控制消息中的「streambegin」,告知客戶端流ID。

d) 播放命令成功的話,服務器發送命令消息中的「響應狀態」 NetStream.Play.Start & NetStream.Play.reset,告知客戶端「播放」命令執行成功。

e) 在此以後服務器發送客戶端要播放的音頻和視頻數據。

能夠注意到裏面音頻type是8,視頻是9。

5. PUBLISH 推流

推流從握手開始和前面步驟123一致。

和第四步play區別在於netstream的命令改成publish

關於本文,若是你在跟隨步驟操做或閱讀時有任何疑問,請點擊這裏跳轉至原文與做者直接交流。

相關文章
相關標籤/搜索