Live555 源代碼分析(二)

1 Socket操作

Live555對常用的socket操作進(jìn)行了包裝。

1.1 setupDatagramSocket()

setupDatagramSocket()創(chuàng)建UDP socket。

  • 調(diào)用createSocket()創(chuàng)建UDP socket
  • 調(diào)用setsockopt()設(shè)置特性:重用地址和端口
  • 調(diào)用MAKE_SOCKADDR_IN()構(gòu)造地址
  • 調(diào)用bind()將socket綁定在這個地址

1.2 setupStreamSocket()

setupStreamSocket()創(chuàng)建TCP socket。

  • 調(diào)用createSocket()創(chuàng)建TCP socket
  • 調(diào)用setsockopt()設(shè)置特性:重用地址和端口
  • 調(diào)用MAKE_SOCKADDR_IN()構(gòu)造地址
  • 調(diào)用bind()將socket綁定在這個地址
  • 調(diào)用fcntl()將socket設(shè)置為非阻塞模式。

1.3 increaseSendBufferTo()

increaseSendBufferTo()調(diào)用setsockopt(),增加發(fā)送緩存的大小。

1.4 writeSocket()、readSocket()

writeSocket()和readSocket()分別發(fā)送和接收數(shù)據(jù)包。

1.5 ourIPAddress()

ourIPAddress()得到本地地址。

  • 調(diào)用setupDatagramSocket()創(chuàng)建UDP Socket
  • 調(diào)用socketJoinGroup(),將socket加入指定組播組。
  • 向組播組發(fā)送數(shù)據(jù)包。因為socket在這個組內(nèi),所以能收到自己發(fā)出的數(shù)據(jù)包。、
  • 調(diào)用select()開始監(jiān)聽。數(shù)據(jù)到達(dá)后返回。
  • 調(diào)用readSocket()接收包,同時接收數(shù)據(jù)包的接收地址。這就是本地地址。
  • 調(diào)用socketLeaveGroup(),退出組播組。
  • 調(diào)用close(),關(guān)閉socket。

2 RTP數(shù)據(jù)收發(fā)

2.1 RTPInterface

RTPInterface負(fù)責(zé)發(fā)送、接收數(shù)據(jù)包。

TCP協(xié)議和UDP協(xié)議都可以承載數(shù)據(jù)包。一個RTPInterface實例可以同時支持多路TCP數(shù)據(jù)和多路UDP數(shù)據(jù)。對于TCP,一個TCP連接上可以有多個channel,每個channel對應(yīng)一路數(shù)據(jù)。

RTPInterface自己處理TCP部分。這其中需要借助tcpStreamRecord和SocketDescriptor。

RTPInterface將UDP部分委托給Groupsock,一個GroupSock實例處理多個UDP連接。

  • 成員fGS是Groupsock實例,用于支持UDP。
  • 成員fTCPStreams是tcpStreamRecord鏈表。 每個tcpStreamRecord實例對應(yīng)一個TCP socket上的channel。

2.2 TCP連接上的數(shù)據(jù)格式

因為不同channel共用同一個TCP連接,所以SocketDescriptor發(fā)送數(shù)據(jù)包時,會加上一個數(shù)據(jù)包頭,其中對不同channel加以區(qū)分。

TCP連接上的數(shù)據(jù)格式如下圖所示。

  • 符號$占一個字節(jié),是數(shù)據(jù)包開始標(biāo)志。
  • channel Id占一個字節(jié),是channel的編號。
  • 數(shù)據(jù)包大小占兩個字節(jié)。Size high 和size low分別是高位字節(jié)和低位字節(jié)。
  • data是調(diào)用者的真正的數(shù)據(jù)。

2.3 用tcpStreamRecord發(fā)送TCP數(shù)據(jù)

RTPInterface::addStreamSocket()創(chuàng)建tcpStreamRecord實例,并注冊到對應(yīng)的SocketDescriptor實例中,以便監(jiān)控socket的可讀數(shù)據(jù)。

  • 使用socket句柄和channel id創(chuàng)建tcpStreamRecord實例,保存在fTCPStreams中。
  • 調(diào)用lookupSocketDescriptor(),查找與socket對應(yīng)的SocketDescriptor實例。
  • 調(diào)用SocketDescritor::registerRTPInterface(),向SocketDescriptor實例注冊RTPInterface實例自身,這里要提供channel編號。
    • 這里將socket置于監(jiān)聽狀態(tài)。當(dāng)有數(shù)據(jù)可讀時,RTPInterface的成員fReadHandlerProc將被調(diào)用。

RTPInterface::sendRTPorRTPPacketOverTCP(),向單個TCP連接發(fā)送數(shù)據(jù)包。

  • 兩次調(diào)用sendDataOverTCP(),依次發(fā)送包頭部分($ + channel id + size high + size low),和數(shù)據(jù)部分(data)。

2.4 用SocketDescriptor接收TCP數(shù)據(jù)

_Tables的成員socketTable保存了全局唯一的SocketDescriptor的hash表。

SocketDescriptor管理一個TCP socket,以及承載在它上面的channel。

對于SocketDescriptor,

  • 成員fOurSocketNum是監(jiān)聽的socket。SocketDescriptor自己不創(chuàng)建這個socket,使用者通過它的構(gòu)造函數(shù)傳給它。
  • 成員fSubChannelHashTable是RTPInterface實例的Hash表。表的key值是channel的編號。
  • registerRTPInterface()向成員fSubChannelHashTable中注冊RTPInterface的channel,這里要同時提供channel的編號。
  • lookupRTPInterface()和deregisterRTPInterface()分別在fSubChannelHashTable中查找和刪除RTPInterface的channel。

SocketDescriptor::registerRTPInterface()還同時將SocketDescriptor::tcpReadHandler()設(shè)置為socket的監(jiān)聽函數(shù)。

當(dāng)socket有數(shù)據(jù)可讀時,SocketDescriptor::tcpReadHandler()被調(diào)用。

  • 因為TCP數(shù)據(jù)可能接收不完整,所以tcpReadHandler()在循環(huán)中反復(fù)調(diào)用tcpReadHandler1(),直到一個完整的數(shù)據(jù)包讀取完成。

在tcpReadHandler1()中,

  • 用一個狀態(tài)機(jī)讀取數(shù)據(jù)包。
    • 讀取字符$、channel id、和size的時候,調(diào)用readSocket()讀取一個字節(jié),根據(jù)當(dāng)前的狀態(tài)保存它們。
    • 讀取數(shù)據(jù)時,讀取數(shù)據(jù)部分,也就是data。
  • 根據(jù)channel編號,調(diào)用lookupRTPInterface()查找對應(yīng)的RTPInterface實例,
  • 調(diào)用RTPInterface的成員fReadHandlerProc,這是使用者提供的回調(diào)函數(shù)。
    • 注意,這時前面的readSockt()已經(jīng)讀出數(shù)據(jù)包的頭,下一次readSocket()讀到的是數(shù)據(jù)包的數(shù)據(jù)(data)。
    • 實際上,使用者RTPInterface指定的回調(diào)函數(shù),將調(diào)用readSocket()讀取數(shù)據(jù)部分。

2.5 用GroupSock收發(fā)UDP數(shù)據(jù)

2.5.1 NetInterface和Socket

NetInterface和Socket定義了網(wǎng)絡(luò)接口。

Port保存端口號,它的成員fPortNum是網(wǎng)絡(luò)字節(jié)序的端口號。

對于NetInterface,

  • 定義了write()接口,用于發(fā)送packet。

對于Socket,

  • 定義了虛擬成員函數(shù)handleRead(),用于接收packet。
  • 成員fPort保存了本地端口號。
  • 成員fSocketNum是創(chuàng)建的socket。

Socket的構(gòu)造函數(shù)調(diào)用setupDatagramSocket()創(chuàng)建socket,綁定本地端口號fPort,地址缺省為INADDR_ANY,即由協(xié)議層決定。

2.5.2 OutputSocket和Groupsock

OutputSocket和GroupSock分別實現(xiàn)了write()和handleRead()。

對于OutputSocket,

  • write()調(diào)用全局函數(shù)writeSocket(),后者調(diào)用sendto()發(fā)送數(shù)據(jù)包。

對于GroupSock,

  • 類destRecord的成員fGroupEid中保存一個組播地址,包括地址和端口號。
  • 成員fDests是一個destRecord鏈表,所以它實際上保存了一個組播地址組。
  • 成員函數(shù)addDestination()和removeDestination()用于從fDests增加和刪除組播地址。

成員函數(shù)output()遍歷fDests中的組播地址,向它們發(fā)送數(shù)據(jù)包。

成員函數(shù)handleRead()負(fù)責(zé)接收消息。它調(diào)用全局函數(shù)readSocket(),后者調(diào)用recvfrom()接收數(shù)據(jù)包。接收的數(shù)據(jù)通過handleRead()返回給調(diào)用者。

2.6 用RTPInterface發(fā)送數(shù)據(jù)

RTPInterface::sendPacket()發(fā)送數(shù)據(jù)包。

  • 調(diào)用GroupSock::output()通過UDP組播發(fā)送數(shù)據(jù)包。
  • 遍歷fTCPStreams,針對其中的TCP連接,依次調(diào)用sendRTPorRTPPacketOverTCP(),發(fā)送數(shù)據(jù)包。

5.7.用RTPInterface接收數(shù)據(jù)

成員函數(shù)startNetworkReading()開始監(jiān)聽所有的TCP和UDP連接。調(diào)用者需要指定有數(shù)據(jù)可讀時的回調(diào)函數(shù)。

  • 這個回調(diào)函數(shù)保存在成員fReadHandleProc中。
  • 調(diào)用TaskScheduler::setBackgroundHandling()將UDP連接置于監(jiān)聽狀態(tài),指定的回調(diào)函數(shù)也是fReadHandlerProc。
  • 遍歷fTCPStreams,調(diào)用lookupSocketDescriptor(),根據(jù)socket值,找到對應(yīng)的SocketDescriptor實例。調(diào)用SocketDescriptor::registerRTPInterface(),注冊channel。

當(dāng)RTCPInterface的UDP或TCP socket有數(shù)據(jù)可讀時,調(diào)用者指定的回調(diào)函數(shù)被調(diào)用。這時它應(yīng)該調(diào)用RTCPInterface::handleRead()讀取數(shù)據(jù)。

  • 每次調(diào)用只讀一個socket,TCP連接優(yōu)先。
    ++ 成員fNextTCPReadStreamSocketNum和fNextTCPReadStreamChannelId保存了當(dāng)前可讀的TCP連接的socket和channel編號。SocketDescriptor::tcpReadHandler()讀取數(shù)據(jù)包的包頭后,會設(shè)置這兩個值。
  • 值得再次說明的是,對于TCP,SocketDescriptor::tcpReadHandler()已經(jīng)讀取了數(shù)據(jù)包的包頭,RTPInterface::handleRead()只需要調(diào)用::readSocket(),繼續(xù)讀取TCP包的數(shù)據(jù)部分。
  • 如果是UDP可讀,則調(diào)用GroupSock::handleRead()讀取。

6 RTCP消息收發(fā)

6.1 RTCPInstance

RTCPInstance從RTPSink的信息構(gòu)造RTCP數(shù)據(jù)包,然后通過RTPInterface發(fā)送出去。OutPacketBuffer作為發(fā)送緩存使用。

對于OutPacketBuffer,

  • 成員fBuf是數(shù)據(jù)包緩存,fPacketStart是數(shù)據(jù)包開始位置,fCurOffset是數(shù)據(jù)包結(jié)束位置,也是當(dāng)前的寫位置。
  • 成員函數(shù)enqueue()向緩存當(dāng)前寫位置寫入數(shù)據(jù),insert()向指定位置寫入數(shù)據(jù)。
  • 成員函數(shù)extract()從指定位置提取位置。

對于RTCPInstance,

  • 成員fRTCPInterface負(fù)責(zé)發(fā)送、接收數(shù)據(jù)包。這是從RTCP連接的Groupsock實例構(gòu)造的,Groupsock實例通過RTCPInstance的構(gòu)造函數(shù)指定。
  • 成員fSink是綁定的RTPSink實例,也是通過RTCPInstance的構(gòu)造函數(shù)指定。
  • 成員函數(shù)addStreamSocket()和removeStreamSocket(),只是在RTPInterface的同名函數(shù)上加了一層包裝。通過它們,可以增加RTCP連接的TCP的客戶端目標(biāo)。
  • 成員fOutBuf是發(fā)送緩存。

6.2 發(fā)送RTCP消息

RTCPInstance::sendReport()是發(fā)送數(shù)據(jù)包的例子。它先調(diào)用addReport()和addSDES()構(gòu)造數(shù)據(jù)包,然后在調(diào)用sendBuiltPacket()發(fā)送它。sendBuiltPacket使用了RTPInterface::sendPacket()。

RTCPInstance::schedule()設(shè)置一個超時任務(wù)。如果某些預(yù)期的事,沒有在指定時間內(nèi)發(fā)生,則觸發(fā)指定的回調(diào)函數(shù)RTCPInstance::onExpire()進(jìn)行重試。

RTCPInstace::onExpire()調(diào)用全局函數(shù)OnExpire()。在OnExpire()中,檢查哪些事件沒有如期發(fā)生,如發(fā)送BYE消息或其他消息沒有成功,則重新嘗試。

6.3 接收RTCP消息

RTCPInstance::setByeHandler()設(shè)置一個回調(diào)函數(shù),收到對應(yīng)消息時,該回調(diào)函數(shù)被調(diào)用。這個回調(diào)函數(shù)保存在成員fByeHandlerTask。

在RTCPInstance的構(gòu)造函數(shù)中,調(diào)用RTCPInterface::startNetworkReading()。其中將RTCPInstance::incomingReportHandler()設(shè)置為數(shù)據(jù)可讀時的回調(diào)函數(shù)。

在RTCPInstance::incomingReportHandler()中,

  • 調(diào)用RTCPInterface::handleRead()讀取數(shù)據(jù)到本地緩存。
  • 調(diào)用processIncomingReport()處理緩存。如果是bye消息,調(diào)用fByeHandlerTask()進(jìn)行處理。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容