crtmpserver 執行過程簡明分析

yanlinembed發表於2018-05-14
crtmpserver.cpp main()函式下的執行過程:

1. SRAND(); //隨機數產生過程
2. InitNetworking(); //初始化非同步socket資源,呼叫介面WSAStartup()
3. Variant::DeserializeFromCmdLineArgs(argc, argv, gRs.commandLine) //把命令列帶入的引數,序列化到Variant gRs.commandLine
4. NormalizeCommandLine(configFile) //把序列化的引數檢查一遍,並把按string型別儲存的轉化為實際型別儲存string configFile
5. Initialize() //主要用於初始化執行前的環境、狀態及完善配置資訊
6. Run() //迴圈監聽執行socket的狀態,並及時進行服務處理
7. Cleanup() //系統退出時,需要做的一些清理工作

crtmpserver.cpp Initialize()函式下的執行過程:

1. Logger::Init(); //用於例項化一個Logger物件
2. Logger::AddLogLocation(pLogLocation); //根據命令列有無”–use-implicit-console-appender”,去新增一個ConsoleLogLocation * pLogLocation 物件。
3. gRs.pConfigFile = new ConfigFile(NULL, NULL); //例項化一個空的配置檔案物件
4. splitFileName(configFilePath, fileName, extension); //分割檔名和檔案型別
5. gRs.pConfigFile->LoadLuaFile(configFilePath, (bool)gRs.commandLine[“arguments”][“–daemon”]) //load一個lua配置檔案,並把所有配置資訊放入到pConfigFile指向的物件的Variant _configuration中,並檢查配置引數的合理性。
6. gRs.pConfigFile->ConfigLogAppenders()
遍歷FOR_MAP(_logAppenders, string, Variant, i)變數,並根據配置資訊建立的例項物件(eg. ConsoleLogLocation, FileLogLocation)。
7. IOHandlerManager::Initialize()
主要是把FdStats IOHandlerManager::_fdStats、fd_set IOHandlerManager::_readFds
fd_set IOHandlerManager::_writeFds三個物件初始化,
_pTimersManager = new TimersManager(IOHandlerManager::ProcessTimer)、_isShuttingDown = false,其中_fdStats為fd的統計資訊物件,_readFds為讀取的socket集,_writeFds為寫出的socket集。
8. gRs.pConfigFile->ConfigModules()
遍歷FOR_MAP(_applications, string, Variant, i)變數,把map<string, Module> _modules填充滿,並呼叫Module.Load()函式,把每個應用模組對應的dll載入進來,並根據載入的dll控制程式碼,獲取相應的介面函式指標去初始化getApplication、getFactory兩個函式指標。
9. gRs.pProtocolFactory = new DefaultProtocolFactory()
例項化一個DefaultProtocolFactory物件,DefaultProtocolFactory—>BaseProtocolFactory(建構函式中只產生一個uint32_t _id)。
10. ProtocolFactoryManager::RegisterProtocolFactory(gRs.pProtocolFactory)
把第9步建立的預設協議工廠物件註冊到協議工廠管理器中,註冊過程中,首先檢查協議工廠_id是否已經存在於_factoriesById中,如 果沒有就繼續註冊下去,協議工廠呼叫HandledProtocolChains()介面,返回支援的協議鏈名的向量,並檢查每個協議鏈名是否存在於_factoriesByChainName中,如果沒有就繼續註冊下去,然後協議工廠呼叫HandledProtocols()介面去返回支援的協議id的向量,並檢查每個協議id是否存在於_factoriesByProtocolId變數中,如果沒有就註冊成功。

map<uint32_t, BaseProtocolFactory *> ProtocolFactoryManager::_factoriesById;
map<uint64_t, BaseProtocolFactory *> ProtocolFactoryManager::_factoriesByProtocolId;
map<string, BaseProtocolFactory *> ProtocolFactoryManager::_factoriesByChainName;

11. gRs.pConfigFile->ConfigFactories()
遍歷FOR_MAP(_modules, string, Module, i)變數,根據第8步獲得的getFactory函式指標去生成一個協議工廠賦值給Module中的協議工廠變數BaseProtocolFactory *pFactory,並把此協議工廠變數註冊到協議工廠管理器當中。
12. gRs.pConfigFile->ConfigAcceptors()
遍歷FOR_MAP(_modules, string, Module, i)中的變數,再遍歷FOR_MAP(config["acceptors"], string, Variant, i)中的變數,根據
acceptors對應的每一個形如

{
    ip="0.0.0.0",
    port=1935,
    protocol="inboundRtmp"
}

的變數執行BindAcceptor()介面,過程如下:
1. 首先根據協議鏈名呼叫
vector<uint64_t> chain = ProtocolFactoryManager::ResolveProtocolChain(node[CONF_PROTOCOL]);生成
協議鏈名對應的協議id所構成的向量,然後判斷向量的第一個元素是TCP還是UDP協議去生成相應的TCPAccpetor物件。
2. TCPAcceptor *pAcceptor = new TCPAcceptor(node[CONF_IP], node[CONF_PORT], node, chain);
初始化IOHandler的_id、_type = IOHT_ACCEPTOR,並記錄埠、IP地址、協議id向量、acceptor子節點引數;並把此IOHandler註冊到IOHandlerManager中,註冊過程就是:
首先,根據IOHandler的_id註冊到map<uint32_t, IOHandler *> IOHandlerManager::_activeIOHandlers;
然後,根據IOHandler的_type呼叫_fdStats.RegisterManaged(pIOHandler->GetType());註冊到_fdStats中,更新其統計。
3. 呼叫pAcceptor->Bind()介面,
首先,完成socket建立,並賦值給_inboundFd、_outboundFd變數;
然後,setFdOptions(_inboundFd, false),
然後,繫結bind(_inboundFd, (sockaddr *) & _address, sizeof (sockaddr)
最後,監聽listen(_inboundFd, 100)
13. gRs.pConfigFile->ConfigInstances()
do nothing, windows server don’t support multiple instance.
14. IOHandlerManager::Start()
do nothing.
15. gRs.pConfigFile->ConfigApplications()

1. pApplication = getApplication(config); // 應用物件的建立

遍歷FOR_MAP(_modules, string, Module, i)中的_modules,呼叫每個Module的介面函式ConfigApplication(),在其內部呼叫第8步配置的函式指標getApplication去生成應用程式物件(eg. AdminApplication),應用程式物件都是繼承自BaseClientApplication類。
在此物件建立過程中,AdminApplication只是把_pRTMPHandler = NULL (型別為RTMPAppProtocolHandler *),
然後其基類物件BaseClientApplication完成的事情就比較多:_id(應用id編號)、_configuration(此應用的配置)、_name(應用名)、_aliases(應用程式別名)、_isDefault(是不是預設應用)、
_allowDuplicateInboundNetworkStreams(是否允許複製入站網路流)、_hasStreamAliases(流別名)
基類物件有一個型別為StreamsManager類的變數_streamsManager(this = 例項物件AdminApplication) ,其建構函式也僅僅是記錄實際的應用_uniqueIdGenerator = 1、_pApplication = AdminApplication;

2. ClientApplicationManager::RegisterApplication(pApplication) //應用物件新增到ClientApplicationManager

a. 檢查應用的id是否在_applicationsById; b. 檢查應用的name、aliases是否在_applicationsByName c. 檢查是否是預設應用

3. pApplication->Initialize()

a. BaseClientApplication::Initialize() //do nothing
b. _pRTMPHandler = new RTMPAppProtocolHandler(_configuration) (BaseAppProtocolHandler*)
RTMPAppProtocolHandler()建構函式do nothing
BaseRTMPAppProtocolHandler()建構函式記錄以下變數值:
_keyframeSeek、_clientSideBuffer、_seekGranularity、_mediaFolder、_renameBadFiles、_externSeekGenerator、
_enableCheckBandwidth、_lastUsersFileUpdate等變數
BaseAppProtocolHandler()建構函式記錄_configuration = configuration
c. RegisterAppProtocolHandler(PT_INBOUND_RTMP, _pRTMPHandler)
往應用程式的map<uint64_t, BaseAppProtocolHandler *> _protocolsHandlers新增Handler,並設定此Handler的應用為應用物件本身。
d. RegisterAppProtocolHandler(PT_OUTBOUND_RTMP, _pRTMPHandler)

4. pApplication->ParseAuthentication()

認證暫時不做分析,等以後有時間再做處理。

5. pApplication->ActivateAcceptors(acceptors)

把應用中acceptors的每一個acceptor都啟用,根據acceptor的型別去做相應的處理,以IOHandler的型別為IOHT_ACCEPTOR做講解:
a. 首先設定此IOHandler的應用為本應用物件(this)
b. 呼叫此IOHandler的StartAccept()介面,主要是設定好map<int32_t, map<uint32_t, uint8_t> > _fdState關於此Handler,然後更新FdSets. <Handler關聯的socket, <Handler的id, Handler對應的狀態>>

16. installQuitSignal(QuitSignalHandler) //註冊好退出處理函式

crtmpserver.cpp Run()函式執行過程
1. IOHandlerManager::Pulse() //根據此函式的執行結果決定迴圈是否繼續

a. 清空_readFdsCopy、_writeFdsCopy,
b. 把_readFds、_writeFds賦值給_readFdsCopy、_writeFdsCopy
c. 再呼叫select(MAP_KEY(--_fdState.end()) + 1, &_readFdsCopy, &_writeFdsCopy, NULL, &_timeout)進行socket輪詢
d. 遍歷FOR_MAP(_activeIOHandlers, uint32_t, IOHandler *, i),檢查IOHandler所對應的socket是否在其中,
FD_ISSET(MAP_VAL(i)->GetInboundFd(), &_readFdsCopy) 或者 FD_ISSET(MAP_VAL(i)->GetOutboundFd(), &_writeFdsCopy)
e. 如果存在fdSets中,就呼叫相應IOHandler的OnEvent
e.1. accept(_inboundFd, &address, &len)
e.2. setFdOptions(fd, false)
e.3. BaseProtocol *pProtocol = ProtocolFactoryManager::CreateProtocolChain(_protocolChain, _parameters)
e.4. 根據accept返回的fd建立TCPCarrier()例項物件,建構函式裡面就把設定為可讀資料狀態、註冊到IOHandlerManager中
e.5. pTCPCarrier->SetProtocol(pProtocol->GetFarEndpoint());設定pTCPCarrier的協議為遠點協議TCPProtocol
e.6. pProtocol->GetFarEndpoint()->SetIOHandler(pTCPCarrier); 設定遠點協議的IOHandler為TCPCarrier
e.7. pProtocol->GetNearEndpoint()->SetApplication(_pApplication); 設定近點協議的應用為本身應用物件(AdminApplication)
e.8. pProtocol->GetNearEndpoint()->GetOutputBuffer()不為NULL,則pProtocol->GetNearEndpoint()->EnqueueForOutbound();
舉例:GetNearEndpoint返回InboundRTMPProtocol物件,GetOutputBuffer()實際是呼叫BaseRTMPProtocol物件中的IOBuffer _outputBuffer;EnqueueForOutbound()實際呼叫_pFarProtocol->EnqueueForOutbound(),_pFarProtocol為TCPProtocol(),呼叫TCPProtocol::EnqueueForOutbound(),即是_pCarrier->SignalOutputData(),此函式只是ENABLE_WRITE_DATA
f. 如果在OnEvent()中處理失敗就呼叫EnqueueForDelete()進入狀態清理工作,主要是清理_fdState中關於此socket的狀態(相應的去設定_readFds、_writeFds的狀態),並把IOHandler加入到_deadIOHandlers MAP中

然後在TCPCarrier中做資料的收發動作!根據讀寫進行相應的操作:
讀:

TCPCarrier的_pProtocol為TCPProtocol
a. IOBuffer *pInputBuffer = _pProtocol->GetInputBuffer(); //近點協議帶有關於輸入的IOBuffer _inputBuffer
以上實際呼叫TCPProtocol::GetInputBuffer()介面,然後返回TCPProtocol物件的成員 _inputBuffer
b. if (!pInputBuffer->ReadFromTCPFd(_inboundFd, _recvBufferSize, _ioAmount))
c. _pProtocol->SignalInputData(_ioAmount)
在函式內會呼叫_pNearProtocol->SignalInputData(_inputBuffer),對應呼叫BaseRTMPProtocol::SignalInputData()介面,在此函式內部進行輸入資料的解析。

寫:

a. pOutputBuffer = _pProtocol->GetOutputBuffer()
在函式內部呼叫_pNearProtocol->GetOutputBuffer(),實際呼叫BaseRTMPProtocol::GetOutputBuffer(),返回
BaseRTMPProtocol的成員IOBuffer _outputBuffer
b. pOutputBuffer->WriteToTCPFd(_outboundFd, _sendBufferSize, _ioAmount)

2. IOHandlerManager::DeleteDeadHandlers(); //
3. ProtocolManager::CleanupDeadProtocols(); //

相關文章