天天看點

RTI_DDS自定義插件開發 4 接收方

本節介紹用于接收消息的Transport Plugin接口。

RecvResource概念

    用于發送的SendResource的補充是用于接收的RecvResource。 RecvResource必須連結(關聯)到一個特定的端口(也可能是多點傳播位址),以接收通過傳輸發送給應用程式的消息。 與SendResources類似,傳輸插件可以共享多個端口的RecvResource。 對于NDDS希望接收消息的新端口,NDDS将反複調用share_recvresource_rrEA()來共享現有的RecvResource,或者,如果Transport Plugin不能共享,則最後請調用create_recvresource_rrEA() 。

    請注意 ,由于傳輸接收到的消息的目标位址将是Transport Plugin管理的接口之一的位址(請參閱使用多接口接收 ),是以隻需定義一個端口即可定義RecvResource。 端口隻是NDDS配置傳輸的一種方式,以便在消息到達插件後進一步路由消息。 是以,所有發送到同一端口的消息應該通過相同的RecvResource接收。 使用哪些特定的端口号以及為什麼通過高于傳輸插件的NDDS層來确定。

    NDDS将建立單獨的線程以在不同的RecvResources上接收。 是以,對不同RecvResources的receive_rEA()将由不同的線程完成。

    為了了解NDDS多久嘗試為一個Transport Plugin的特定執行個體共享或建立一個RecvResource的想法,請考慮一個簡單的NDDS應用程式和一個參與者。

    當建立參與者時,NDDS将嘗試首先共享,然後在共享之後建立一個RecvResource,用于每個Transport Plugin執行個體的單點傳播中繼資料流量。 如果使用配置,NDDS也将嘗試共享/建立RecvResources以實作多點傳播中繼資料流量。

    然後,如果應用程式具有資料讀取器,NDDS将共享/建立RecvResource以在預設端口上接收消息。 預設情況下,将隻為所有DataReader建立一個。 但是,使用者可以将DataReader配置為在預設使用以外的端口上接收消息。 使用者還可以配置DataReader以使用多點傳播位址接收消息。 當這些資料讀取器被建立時,NDDS将嘗試為不同的端口和/或多點傳播位址共享/建立RecvResources。

    Transport-Plugin實作決定是否需要為不同的端口/多點傳播位址建立不同的RecvResource,或者是否可以共享現有的RecvResource。 再次請注意,對于建立的每個RecvResource,NDDS将使用不同的線程從RecvResource接收消息。

    如果應用程式具有DataWriter,則NDDS還将建立預設的RecvResource以接收與可靠的DataWriter相關的消息。 此RecvResource用于接收來自相應DataReader的中繼資料(如來自支援可靠消息傳遞的确認)。 預設情況下,為所有DataWriters建立一個RecvResource。 但是,像DataReaders一樣,使用者可以配置DataWriter以在不同的端口上使用它來擷取其中繼資料。 對于那些DataWriters,當它們被建立時,NDDS将嘗試共享/建立指定端口的RecvResource。

    在DataWriter和DataReader中使用預設配置和多點傳播的最簡單應用程式未被使用時,NDDS将嘗試共享/建立3個RecvResources(一個用于發現中繼資料流,一個用于DataWriter中繼資料流,另一個用于DataReader)。 如果建立的DataReader和DataWriters配置為接收除預設單點傳播端口和/或使用多點傳播之外的任何消息,則NDDS将嘗試共享/建立更多RecvResources。

    要求建立RecvResource時,Transport Plugin應該做什麼?

    實質上,傳輸插件必須建立它所需的所有資源,并初始化底層傳輸硬體和軟體,以開始接收并存儲由另一個傳輸插件執行個體發送的消息(排隊)。 用于收集這些消息的Transport-Plugin特定資源的句柄作為RecvResource傳遞回NDDS。 當NDDS的上層準備從RecvResource中檢索收到的消息時,NDDS将調用receive_rEA() 。

    通常,RecvResource表示傳入消息的FIFO隊列,這些消息被發送到與RecvResource關聯的端口。 隊列的大小取決于實作。 這可以是建立時傳輸插件集的一個屬性,甚至可以為更複雜的實作動态調整。

接收多個接口

    如果一個傳輸器管理多個接口,那麼當NDDS建立一個RecvResource時,它期望傳輸插件準備接收發往所有接口給定端口的消息。

    例如,如果将串行傳輸插件配置為管理多個串行端口并是以具有多個接口,那麼當為串行傳輸插件建立RecvResource時,該插件應該做它需要做的以開始接收來自所有序列槽。

阻止接收

    NDDS核心将調用receive_rEA()來收集由RecvResource存儲的消息。 請注意,如果RecvResource已共享,則RecvResource可能會存儲針對不同目的地收到的消息。

    當receive_rEA() ,它應該從RecvResource的隊列中傳回一條消息。 如果在隊列中沒有等待消息,則receive_rEA()必須阻止調用者線程,直到傳輸接收到消息。

    如果消息比NDDS核心呼叫receive_rEA()更快地收到RecvResource以收集消息,則Transport Plugin可能會用盡記憶體(隊列中的空間)來存儲傳入的消息。 在這種情況下,傳輸插件被允許悄悄丢棄消息。 如果NDDS确定它需要(例如,支援可靠的消息傳遞),它本身将重新發送消息。

    傳輸插件必須實作unblock_receive_rrEA()函數,作為解除阻塞調用receive_rEA()函數的線程的一種方式,即使沒有收到消息。 主要用于關閉時,NDDS核心中的另一個線程将調用unblock_receive_rrEA()函數來喚醒接收線程。

    receive_rEA()函數應該知道它被unblock_receive_rrEA()喚醒并傳回訓示沒有收到消息(0位元組)。

    請注意 ,如果unblock_receive_rrEA()并且receive_rEA()沒有目前阻塞的線程,則下一次對receive_rEA()必須傳回,表明沒有收到消息 - 即使隊列中收到消息。 這意味着Transport Plugin必須記住調用了unblock_receive_rrEA() ,但是沒有線程被調用喚醒。當接收線程最終調用時receive_rEA(),先前的調用unblock_receive_rrEA()将receive_rEA()立即傳回,表示沒有收到消息(即使接收隊列中有消息)。

    如果線程試圖關閉NDDS調用unblock_receive_rrEA(),但接收線程正忙于處理接收到的消息,則需要此“記憶體”(如果信号量用于阻止接收線程,則可使用計數信号量來實作此功能)實際上并未被阻止receive_rEA()

接收到連續緩沖區

    與傳輸插件的發送端的收集 - 發送設計相反,接收端必須總是将接收到的消息存儲到一個連續的存儲區域(緩沖區)中。

    一個傳輸插件實作可能會發送一條消息,但它需要,例如,作為一個單一的資料包,多個資料包,或者一個包含标題和分隔符的位流。接收端應該收集收到的位元組,直到收到完整的消息并存儲在連續的存儲單元中。隻有這樣,消息才能存儲在隊列中供以後通過receive_rEA()呼叫檢索。

    如果傳輸插件确定消息的一部分丢失或已被破壞,則應丢棄整個消息。不應将不完整或部分消息傳遞給NDDS。

可選的“Loaned-Buffer”機制

    在receive_rEA()調用中,Transport-Plugin實作可以通過将消息複制到作為參數傳入的NDDS緩沖區,将接收到的消息傳回給NDDS buffer_in。可選地,傳輸插件可以忽略buffer_in,而是通過傳回一個指向消息(已經)存儲的“内部”緩沖區的指針來傳遞接收到的消息。

    這種方法被稱為“借用緩沖區”到NDDS核心。當NDDS核心處理完貸款緩沖區中的消息後,它将調用傳輸插件提供的函數plugin->return_loaned_buffer_rEA(),将借出的緩沖區傳回給插件。在NDDS receive_rEA()再次調用相同的RecvResource 之前,一個借出的緩沖區總是會被傳回。

    使用“借用緩沖”機制的主要原因是通過傳輸插件向NDDS核心提供高效的零拷貝接收實作。

    一些實體傳輸在裝置驅動程式級别上已經有一個零拷貝接口。例如,在某些作業系統(如VxWorks)中,IP堆棧本身可以借用包含接收到的消息的單個緩沖區。在這種情況下,Transport Plugin應該能夠将此緩沖區直接傳遞到NDDS核心,而不需要中間副本。這是“零拷貝”優化。

是以運輸可以分為三類。

    沒有零拷貝。在這種情況下,傳輸插件必須始終使用NDDS提供的緩沖區來傳回消息。不會發生傳輸内部緩沖區的借用。插件實作應該将用于傳回一個借用緩沖區的函數指針設定為NULL,NDDS_Transport_PluginImpl :: return_loaned_buffer_rEA,表示在NDDS處理接收到的消息之後不需要做任何事情。

    總是可以零複制。當receive_rEA()調用檢索收到的消息時,一些傳輸将始終将傳輸内部緩沖區租借給NDDS核心。這些緩沖區通常是實體傳輸本身用于存儲接收到的消息的緩沖區。是以這類傳輸可以提供處理收到的消息的最有效的方式。如果一個傳輸插件承諾總是借用一個緩沖區,NDDS核心的記憶體效率會更高,并且不會配置設定一個buffer_in插件永遠不會用來複制收到的消息的緩沖區()。這樣的傳輸應該在NDDS_Transport_Property_t :: properties_bitmap,NDDS_TRANSPORT_PROPERTY_BIT_BUFFER_ALWAYS_LOANED中設定一點,讓NDDS核心知道它會一直傳回借用緩沖區中的消息。

    有時可以零複制。一些傳輸可以對某些消息使用零複制方式,但對其他傳輸方式則不行。例如,支援零拷貝的IP堆棧可能會在非連續緩沖區中存儲傳入消息。沒有首先将消息複制到單個連續緩沖區(可能在NDDS提供的緩沖區中),傳輸插件将無法将此消息發送給NDDS receive_rEA()。在這種情況下,Transport Plugin不應該設定NDDS_Transport_Property_t :: properties_bitmap的NDDS_TRANSPORT_PROPERTY_BIT_BUFFER_ALWAYS_LOANED位。相反,它将選擇使用NDDS提供的緩沖區,或根據傳輸接收消息的方式将消息傳回到借用緩沖區中。

NDDS核心本身将以相當透明的方式處理這三種情況。除非為特定傳輸插件設定了NDDS_TRANSPORT_PROPERTY_BIT_BUFFER_ALWAYS_LOANED位,receive_rEA()否則将始終使用足夠大的緩沖區來調用傳輸插件配置為接收的最大消息。

    如果傳輸插件實作安裝了NDDS_Transport_PluginImpl :: return_loaned_buffer_rEA函數,那麼如果傳輸已經在該receive_rEA()函數中為NDDS提供了一個緩沖區,return_loaned_buffer_rEA()那麼在完成消息處理後NDDS将會調用。

    如果在消息沒有 貸款,并且return_loaned_buffer_rEA()應該不叫,然後運輸插件必須設定的值loaned_buffer_param NDDS_Transport_Message_t ::至-1。NDDS将解釋任何其他值作為消息中的緩沖區被借出并return_loaned_buffer_rEA()用該消息呼叫的訓示。

繼續閱讀