通過分析feedbackexample例程來了解EMIPLIB庫。feedbackexample例子可以實現(xiàn)在本機(jī)播放1個wav文件。但這類播放不是簡單的調(diào)用本機(jī)播放API來實現(xiàn),而是利用EMIPLIB庫提供的RTP框架來完成。在不了解細(xì)節(jié)前,可以將這個進(jìn)程簡單理解成這樣:讀取本地的1個wav文件,然后將其打包成RTP,再將這些RTP發(fā)送到feedbackexample監(jiān)聽的1個UDP端口,feedbackexample再解析這些RTP包,并還原成可以播放的語音數(shù)據(jù)提交給本機(jī)聲卡。我通常在這類場合下使用這個例程。其他程序通過SIP協(xié)議注冊到1個語音交換平臺,只實現(xiàn)了SIP協(xié)議交互部份并未實現(xiàn)RTP。這時候,就能夠借用feedbackexample例程,只需將feedbackexample例程監(jiān)聽的端口號和對真?zhèn)€IP地址和端口號都設(shè)置成另外一個程序中SIP協(xié)議交互得到的端口號便可。也就是說,通過這樣兩個簡單的程序可以實現(xiàn)1個非常簡易的SIP軟電話。1個程序負(fù)責(zé)SIP信令,另外一個程序負(fù)責(zé)RTP語音傳輸。
這個例程很簡單,但也包括了足夠的信息去了解EMIPLIB的運(yùn)作細(xì)節(jié)。接下來看看這些代碼究竟是如何做到的。
這個例程的代碼非常簡單,只有3個額外的輔助函數(shù),和1個類。這3個輔助函數(shù)完全可以不用去了解,這個類也只是為了輸出1些日志信息而已并沒有增加其他功能性方面的代碼。那關(guān)注點就能夠落在main函數(shù)里了。進(jìn)入到main函數(shù)內(nèi),去除那些注釋后,真正有用的代碼從變量定義開始。
這些類型都是EMIPLIB庫提供的類。由于我們只是探究如何發(fā)送,所以這里列出來的這么多類型,我們只會關(guān)注1部份:
未列出的那些類型都是解碼相干的類。還得再重點說說MyChain類。這是個繼承類,父類是MIPComponentChain。名稱中最后的單詞是Chain。Chain是鏈、鏈條的意思。這個類是EMIPLIB運(yùn)行時框架的核心類。是它將各個類組合在1塊,記錄像互關(guān)系,調(diào)和各個類的履行前后順序等。稍后我們將詳細(xì)研究這個類的源碼。
接下來的代碼就是初始化這些變量。中間會出現(xiàn)1些RTP開頭的類:
這些是另外一個庫jrtplib提供的類。jrtplib庫也是EMIPLIB庫的底層基礎(chǔ)。這3個類的目的就是為了在網(wǎng)絡(luò)上傳輸接收RTP包。由于我們這次研究的是EMIPLIB,所以這些類就不細(xì)說了。
初始化完后,接著就是調(diào)用MIPComponentChain的addConnection操作這些變量。類似于這樣:
注釋的意思是這是在創(chuàng)建鏈條。最后的啟動是調(diào)用MIPComponentChain的start。
main函數(shù)內(nèi)的代碼基本情況就是這樣。分3個部份:初始化變量,構(gòu)建1個鏈條,啟動這個鏈條。現(xiàn)在雖然沒有查看MIPTime和MIPWAVInput等類的聲明,基于鏈條的概念,和MIPComponentChain類名的暗示,可以猜想出之條件到的那些MIP開頭的類應(yīng)當(dāng)都是類似于MIPComponent類的子類。還有1個可以解釋成鏈條、任務(wù)流的地方是這些類名。聲明的順序是MIPWAVInput、MIPSamplingRateConverter、MIPSampleEncoder、MIPULawEncoder、MIPRTPULawEncoder和MIPRTPComponent。根據(jù)這個順序我們分明看到了1個清晰的將本地wav文件打包成RTP數(shù)據(jù)包的任務(wù)流程。這也是1個左證,MIPComponentChain是1個任務(wù)流履行體、任務(wù)鏈條。但這些畢竟是猜想,實際情況是否是正如猜想的那樣,還得分析了實現(xiàn)源碼再說。
feedbackexample例程中使用了MIPComponentChain3個成員函數(shù):setChainStart、addConnection和start。通過它們的名稱我們可以立即知曉這3個函數(shù)的意圖。第1個應(yīng)當(dāng)是設(shè)置1個出發(fā)點。最后1個是啟動。第2個有點模糊,加1個connection。輸入?yún)?shù)是兩個Component(基于我們之前的猜想)。connection的意思是連接、聯(lián)系。這么來理解的話,這第2個函數(shù)的作用就是建立兩個component的關(guān)系?,F(xiàn)在都是猜想,接下來看代碼。
這個函數(shù)確切很簡單。就是將輸入?yún)?shù)賦值給m_pInputChainStart成員變量。
注意到1點,輸入?yún)?shù)的類型是MIPComponent。猜想是對的,而且名稱完全猜對了。
這個也很簡單,就是將兩個MIPComponent放入1個list內(nèi)。inputConnections的類型是個std::list。之所以說簡單,是由于暫時疏忽后3個參數(shù)。
起始處調(diào)用了JThread的IsRunning函數(shù)。難不成MIPComponentChain類繼承自JThread?查看這個類的頭文件。果然繼承自jthread類。JThread類也是由jrtplib庫提供。
起始處的兩個判斷很容易理解。代碼中我已添加了注釋。接著是定義兩個std::list。然后是順次調(diào)用orderConnections、buildFeedbackLis和copyConnectionInfo這3個成員函數(shù)。最后是調(diào)用JThread::Start()函數(shù),這個函數(shù)很明顯目的就是啟動1個線程。在沒有具體分析orderConnections、buildFeedbackLis和copyConnectionInfo這3個成員函數(shù)前,對MIPComponnetChain::start函數(shù)的粗略認(rèn)識就是終究要啟動1個線程,但在啟動線程前要做1些準(zhǔn)備工作。那末接下來就看看到底在啟動線程前都做了些甚么。先看看orderConnections。
這個函數(shù)內(nèi)的第1步是遍歷m_inputConnections鏈表。之前這個鏈表在addConnection函數(shù)內(nèi)出現(xiàn)過?,F(xiàn)在可以回過頭再去看看,那是將兩個MIPComponent變量組合成1個MIPConnection變量,再放入m_inputConnections鏈表內(nèi)。在這里調(diào)用了MIPConnection的setMark函數(shù),應(yīng)當(dāng)是將內(nèi)部mark屬性置為false。具體有何目的現(xiàn)在還不知道。
接著往下看。componentLayer是在函數(shù)起初處定義的,類型是std::list<MIPComponent *>。這是1個存儲MIPComponent指針的鏈表。m_pInputChainStart之前在setChainStart函數(shù)內(nèi)出現(xiàn)過,這是存儲起始節(jié)點的變量?,F(xiàn)在它被第1個放入了componentLayer鏈表內(nèi)。
然后是1個while循環(huán)。為了看著方便,下面的代碼片斷去除源碼中具有的日志輸出語句。
結(jié)束上面這個while循環(huán)的條件是componentLayer鏈表為空。這個循環(huán)之前的1句是將起始節(jié)點放入這個鏈表內(nèi)。也就是說,進(jìn)入循環(huán)前鏈表的初始狀態(tài)是有1個元素在鏈表內(nèi)。接著是兩個嵌套的for循環(huán)。循環(huán)的目的是將每一個componentLayer鏈表內(nèi)的元素取出后,再在m_inputConnections鏈表內(nèi)遍歷1次做些操作。第2層for循環(huán)的第1句的注釋很清楚表明了目的,檢查這個MIPConnection是不是被處理過。這也說明了這個函數(shù)內(nèi)第1步的目的:確保m_inputConnections鏈表內(nèi)所有的MIPConnection在處理前的處理標(biāo)志變量值都是“未處理”,也就是將mark置為false。
如果這個MIPConnection未被處理過,繼續(xù)下面的判斷,檢查這個MIPConnection的“pull componnet”是否是當(dāng)前正在檢查的MIPComponent。這個被檢查的MIPComponent是從componentLayer鏈表內(nèi)取出來的。由于初始時componentLayer內(nèi)只有初始節(jié)點,也就是說這兩個for循環(huán)的第1次迭代就是找到那個pull component是初始節(jié)點的MIPConnection。找到這個MIPConnection后將它的mark標(biāo)志置為true,也就是說這個MIPConnection已處理過了,后續(xù)再迭代時不要再處理它。然后將這個MIPConnection放入orderedList鏈表內(nèi)。orderedList也是在函數(shù)開始處定義的,類型是std::list<MIPConnection>,也是個鏈表,存儲的是MIPConnection變量。然后取出這個MIPConnection內(nèi)的“push component”,又進(jìn)入另外一個for循環(huán),目的是在newLayer鏈表內(nèi)尋覓是不是存在這個“push component”。如果newLayer內(nèi)不存在這個“push component”,那末將這個“push component”放入newLayer。履行完第1次兩個for循環(huán)的迭代后,再將newLayer賦值給componentLayer。此時,指的是第1次迭代,componentLayer內(nèi)應(yīng)當(dāng)只有1個元素,就是與起始節(jié)點組合成1個MIPConnection的“push component”。由于componentLayer不為空,所以while循環(huán)不會結(jié)束,繼續(xù)下1次迭代。第2次迭代的目的就是找到以這個“push component”為“pull component”的MIPConnection,然后設(shè)置mark標(biāo)志,再取出另外一個“push component”。至此,應(yīng)當(dāng)可以明白這個函數(shù)的目的了,就是以起始節(jié)點為開端找到這個任務(wù)流內(nèi)MIPConnection的履行順序,并順次放入orderedList內(nèi)。最后還有1個for循環(huán),目的是確保不再存在未被處理過的MIPConnection,如果有肯定是哪出錯了。最后將排過序的MIPConnection鏈表輸出到函數(shù)外。
由于最外圍的while循環(huán)肯定要在某種條件下結(jié)束。代碼里顯示結(jié)束的標(biāo)志是componentLayer鏈表為空,而每次循環(huán)最后1條語句是將newLayer變量賦值給componentLayer。也就是說while每次循環(huán)后如果newLayer為空則while循環(huán)結(jié)束。換種說法就是每次whille循環(huán)如果沒有找到下1個push component那末循環(huán)就結(jié)束了。依照這類思路倒推,在建立處理流時最后加入的MIPConnection變量里的push component1定應(yīng)當(dāng)是個空指針。這樣的1個MIPConnection就是在告知處理邏輯,處理流要結(jié)束了。
現(xiàn)在再回過頭去看start函數(shù)內(nèi)調(diào)用這1函數(shù)的目的就是排序m_inputConnections鏈表:從起始的MIPComponent節(jié)點開始,依照MIPConnection類給出的前后依承關(guān)系,理清這個任務(wù)流的順序?,F(xiàn)在已弄明白了start函數(shù)內(nèi)被調(diào)用的第1個函數(shù),接著看下1個buildFeedbackList。
這個函數(shù)需要兩個參數(shù)。1個是之前分析過的orderConnections函數(shù)生成的排過序的MIPConnection鏈表。另外一個是輸出參數(shù),是個存儲MIPComponent指針的鏈表。
函數(shù)起始處是個for循環(huán)。目的依然是設(shè)置orderedList鏈表內(nèi)MIPConnection元素的已處理標(biāo)志。如果MIPConnection的成員函數(shù)giveFeedback返回false,那末設(shè)置MIPConnection已處理過,或說這個MIPConnection可以不用再處理了。想詳細(xì)看看這個giveFeedback的具體實現(xiàn)如何,所有就去看看了。MIPConnection類定義居然是在MIPComponentChain內(nèi)部。giveFeedback只是簡單的返回內(nèi)部成員變量m_feedback的值。這個成員變量值是在MIPConnection類生成時被賦的值。MIPConnection類都是在MIPComponentChain的addConnection函數(shù)內(nèi)被創(chuàng)建的。追溯到這里可以看到MIPConnection的m_feedback值的根源來自addConnection函數(shù)的feedback輸入?yún)?shù)。再檢查feedbackexample例程的代碼,幾近所有編碼進(jìn)程相干的MIPComponent的addConnection操作均沒有設(shè)置這個值,使用的是函數(shù)的缺省參數(shù)false。
但大部份所解碼進(jìn)程相干的MIPComponent的addConnection操作均設(shè)置了feedback輸入?yún)?shù),并且使用的都是true。
中間還夾著著1小段注釋。感覺是解釋feedback機(jī)制的,但看不太懂。到現(xiàn)在還是不知道什么時候和為什么設(shè)置feedback標(biāo)志。接著看下面的代碼。接下來是1大段while。
這段while的最前部是又1個while循環(huán)。遍歷檢查orderedList鏈表,找到第1個未被處理過、且giveFeedback返回true的MIPConnection。如果找到了,就立即結(jié)束這部份處理。從大的while循環(huán)角度看,每次都得再做這么1次處理。也就是說,每次大的循環(huán)開始時,都得在orderedList鏈表內(nèi)找出1個未被處理過、且giveFeedback返回true的MIPConnection。如果有1次遍歷找不到滿足條件的MIPConnection,那末遍歷結(jié)束。
現(xiàn)在我們找到了1個需要處理的MIPConnection。下面列出的代碼去除1些注釋,重新做了些排布工作。首先是設(shè)置這個MIPConnection的處理標(biāo)志為true,下次就不會再找到它了。然后是清空subChain,這是個鏈表,存儲的是指向MIPComponent的指針。然后將這個正在處理的MIPConnection的“pull component”和“push component”順次放入這個subChain。
然后是另外一個while循環(huán)。這次遍歷是從剛才在orderedList中找到的那個MIPConnection的后1個MIPConnection開始。如果排在后面的MIPConnection的“pull component”等于這個被找到的MIPConnection的“push component”,這個排在后面的MIPConnection的處理標(biāo)志被置為true,且它的“push component”被放入subChain鏈表內(nèi)。但如果后面有兩個滿足這樣條件的MIPConnection,說明有錯。如果還需遍歷,會從在這次遍歷中找到的MIPConnection的后面1個MIPConnection開始,且startIt存的是這次遍歷中找到的MIPConnection。也就是說,subChain存的是那些MIPConnection feedback標(biāo)志為true的所有MIPComponent,且先放入“pull component”再放入“push component”。
在每次大的循環(huán)迭代處理過后,subChain存的是那些在這次迭代開始時在orderedList中找到的第1個feedback為true的MIPConnection的“pull component”,且以這個MIPComponent為出發(fā)點的子任務(wù)鏈。這個子任務(wù)鏈?zhǔn)侨咳蝿?wù)鏈的1小部份,它是整體的子集。這個子任務(wù)鏈的結(jié)束標(biāo)志有兩個,1個是到達(dá)了鏈尾,另外一個是遇到了1個feedback標(biāo)志為false的MIPConnection。我想這個處理進(jìn)程應(yīng)當(dāng)是想找到所有這樣的子鏈。這樣的存在有它本身的意義,現(xiàn)在還不得而知。
最后1部份有點意思。將某次迭代中找到的subChain內(nèi)元素按倒序方式放入feedbackChain鏈表內(nèi)。兩兩子鏈以1個0做為分割。最后將feedbackChain內(nèi)元素拷貝給函數(shù)的第2個參數(shù)feedbackComponentChain。
這個函數(shù)的目的略微清晰了點。找出1些子鏈,這些子鏈再按倒序放入1個鏈表內(nèi),子鏈間以0分隔。現(xiàn)在可以繼續(xù)start函數(shù)內(nèi)出現(xiàn)的最后1個函數(shù)了,copyConnectionInfo。
可以說這個函數(shù)非常簡單1目了然。就是將之前兩個函數(shù)orderConnections和buildFeedbackList處理的結(jié)果保存在成員變量中。
至此,start函數(shù)內(nèi)牽涉到的3個成員函數(shù)都看了1遍。大致了解了他們的作用。這些都只是為了啟動線程前的準(zhǔn)備工作。我們知道start函數(shù)的終究目的是啟動1個線程。那末真正線程內(nèi)的處理都是些甚么呢,哪一個函數(shù)是做這件事的呢?掃1遍mipcomponentchain.cpp文件后發(fā)現(xiàn)Thread函數(shù)有點像。再進(jìn)入這個函數(shù)內(nèi)部看到起始處有這么1句日志輸出:
這應(yīng)當(dāng)毫無疑問就是線程的入口函數(shù)了。接下來就看看它到底做了些甚么。
大致看了1眼,再加上之前有過線程編碼的經(jīng)歷,可以推測出這個函數(shù)內(nèi)最主要的部份應(yīng)當(dāng)就是1個循環(huán)。但不多是個無窮循環(huán),肯定有退出機(jī)制。進(jìn)入循環(huán)前應(yīng)當(dāng)有1些初始化操作。
上面這些初始化操作大部份都很好理解。只是又多了1個之前未遇到過的類:MIPSystemMessage。既然出現(xiàn)了,那就看看他的定義。這個類的頭文件里有1段描寫這個類作用的文字。
大致的意思是說,這個類的作用是消耗掉特定時長。有點類似于多線程編程中常常使用的Sleep函數(shù),只不過在這將其封裝成了類。同時還發(fā)現(xiàn),MIPSystemMessage繼承自MIPMessage。MIPMessage類也有1段解釋文字。從中可以看出,MIPMessage是個基類。MIPMessage消息將會從1個MIPComponent傳遞給另外一個MIPComponent。
這同時也說明了鏈條中各個MIPComponent之間是如何通訊的。同時,也應(yīng)當(dāng)注意到MIPComponentChain會生成1個子類型是MIPSYSTEMMESSAGE_TYPE_WAITTIME的MIPSystemMessage對象,并提供給鏈條中的第1個MIPComponent。也就是說,任何1個可以充當(dāng)鏈條中第1個元素的MIPComponent必須可以處理子類型是MIPSYSTEMMESSAGE_TYPE_WAITTIME的MIPSystemMessage對象。以上就是循環(huán)前的初始化操作。接下來進(jìn)入循環(huán)內(nèi)部。此循環(huán)大致由3部份組成。1,針對鏈條初始節(jié)點的操作。2,遍歷m_orderedConnections鏈表。3,遍歷m_feedbackChain鏈表。這3步中任何1步履行完后都會檢查毛病標(biāo)志,用來判斷是不是需要立即結(jié)束循環(huán)。
第1階段。
第1步是調(diào)用初始節(jié)點的push函數(shù)。向其傳入初始化階段生成的startMsg變量。其他加鎖操作就不做分析了。
push操作如果失敗就結(jié)束全部循環(huán),也就是說結(jié)束線程。這1步最關(guān)鍵的1點就是push操作都做了哪些事。必須以1個具體的實例來做說明才能對這1步有清晰的認(rèn)識。正好再回到feedbackexample例程中代碼結(jié)合著來分析。在例程中設(shè)置的初始節(jié)點類型是MIPAverageTimer。打開mipaveragetimer.h文件看看這個類是如何定義的。下面這段內(nèi)容很有幫助。
意思是接收MIPSYSTEMMESSAGE_WAITTIME消息,產(chǎn)生MIPSYSTEMMESSAGE_ISTIME消息。產(chǎn)生消息前必須是經(jīng)過了多少秒后。再看看push和pull兩個函數(shù)的實現(xiàn)。由于這個類也是繼承自MIPComponent。所謂的“接收MIPSYSTEMMESSAGE_WAITTIME消息”是在push函數(shù)內(nèi)實現(xiàn)的。push函數(shù)內(nèi)會判斷輸入的MIPMessage變量類型是不是滿足要求。其次,push函數(shù)內(nèi)還實現(xiàn)了“經(jīng)過了多少秒后”,可以在源碼中看到下面這句:
diff的值由下面方法生成:
m_interval的值在創(chuàng)建MIPAverageTimer時指定,例程中給出的值是0.02。m_startTime的值在創(chuàng)建MIPAverageimer時指定,實際值是創(chuàng)建時的系統(tǒng)時間。iteration是迭代值,每次Thread的for循環(huán)履行1次累加這個值,并傳給所有的MIPComponent對象。curTime是MIPAverageTimer的push函數(shù)被調(diào)用時的時間。diff值的含義是,如果每次迭代都消耗了0.02秒,那末diff值就是這次push被調(diào)用時的時間與理想情況下應(yīng)當(dāng)被消耗的時間的差值。如果這個值大于零,說明實際情況是之前的處理有些快需要減慢,所以就休眠1段時間。
現(xiàn)在再回到第1部份的處理場景中。加上之前針對MIPAverageTimer類的分析。現(xiàn)在能夠明白這第1部份都做了些甚么:履行了MIPAverageTimer的push函數(shù)。startMsg變量正是push函數(shù)所需要的類類型。由于滿足了這些條件,所以push函數(shù)內(nèi)還履行了休眠操作。也就是說,這第1部份的真正作用就是休眠了固定時長。也就是說每次迭代的第1步都是休眠固定時長。
第2階段/
接著看第2步:遍歷m_orderedConnections。現(xiàn)在我們知道這個鏈表存儲的是1個有序的MIPConnectiont集合。其實也是有序的MIPComponent集合。由于前1個MIPConnection的pull component就是后1個MIPConnection的push component。
每次遍歷都會取出1個MIPConnection,目的是得到這個MIPConnection的pull component和push component。然后每次從pull component中取出1個MIPMessage再提供給push component。結(jié)束某1個MIPConnection的處理條件是沒法再從pull component取出MIPMessage,或出錯了。我們模糊能感覺到消息在全部處理鏈條中被傳遞的進(jìn)程。
第3階段。
現(xiàn)在進(jìn)入第3步,feedback鏈條。處理feedback鏈條會用到1個類MIPFeedback。應(yīng)當(dāng)還記得,m_feedbackChain存儲的是多個feedback鏈條,鏈條間以空指針分隔。所以在遍歷處理進(jìn)程中也看到了針對這個情況的處理。每次重新1個新的子鏈條的處理前要重置feedback變量。除此以外每次遍用時,都是調(diào)用鏈條中MIPComponent的processFeedback函數(shù)。processFeedback函數(shù)接收的參數(shù)中就包括MIPFeedback類型的變量。之前已提到過,每次1個新的子鏈條處理前都會重置MIPFeedback變量。也就是說,每一個子鏈條內(nèi)MIPComponent間傳遞消息是通過MIPFeedback類。目前為止還只是猜想。接著看MIPFeedback類的說明。
這段關(guān)于類用處的注釋左證了我們之前的猜想。第3部份的框架其實很簡單。處理m_feedbackChain內(nèi)保存的每一個feedback鏈條。鏈條內(nèi)MIPComponent間通過MIPFeedback傳遞消息。但是,EMIPLIB庫設(shè)計這個feedback鏈條的目的是甚么依然不清晰。
通過分析例程代碼,和庫源碼,大致了解了該如何使用EMIPLIB庫和EMIPLIB庫內(nèi)部實現(xiàn)。現(xiàn)在知道了為了向網(wǎng)絡(luò)的1個端點發(fā)送語音,該使用哪些MIPCompnent組件,該如何建立這些組件間在運(yùn)行期間的關(guān)系。同時,也知曉在運(yùn)行期EMIPLIB將會創(chuàng)建1個線程在后臺履行語音數(shù)據(jù)的轉(zhuǎn)換和發(fā)送?;镜倪M(jìn)程是這樣的。先創(chuàng)建1些語音數(shù)據(jù)轉(zhuǎn)換和發(fā)送用的MIPCompnent組件。然后初始化這些組件實例。接著創(chuàng)建1個MIPComponentChain類實例。指定1個初始MIPComponent節(jié)點。接下來依照順序加入MIPComponeng組件實例。最后1步就是調(diào)用MIPComponentChain類的start函數(shù)。
經(jīng)過上述的分析,現(xiàn)在知道了該如何啟動1個將本地文件打包成RTP并發(fā)送給特定網(wǎng)絡(luò)地址端口的進(jìn)程??梢栽谌问裁磿r候間暫停或停止這樣1個進(jìn)程嗎?我們再看看MIPComponentChain類的源碼,是不是存在1個這樣的成員函數(shù)。發(fā)現(xiàn)還有這樣幾個成員函數(shù)未仔細(xì)分析:
從函數(shù)名稱中可以大致知曉這幾個成員函數(shù)的用處。沒有找到暫停這樣1個進(jìn)程的函數(shù),只是找到了結(jié)束這樣1個進(jìn)程的函數(shù),stop。這4個函數(shù)的內(nèi)部邏輯都非常簡單,就不逐一羅列了。除stop函數(shù)外,其他3個函數(shù)都未在類內(nèi)部被調(diào)用過。
現(xiàn)在已了解到,MIPComponentChain類在EMIPLIB庫中所處的核心位置。這個類實現(xiàn)了EMIPLIB庫的處理框架。它負(fù)責(zé)協(xié)同調(diào)用各個MIPComponent組件。MIPComponent組件間通過MIPMessage消息類傳遞信息。這篇分析文章只是揭露了EMIPLIB在高層是如何運(yùn)作的。接下來有必要深入到具體的MIPComponent類源碼中1探究竟。