ACE_Message_Block功能簡介

ACE_Message_Block在Ace中用來表示消息的存放空間,可用做網絡通信中的消息緩衝區,使用非常頻繁,下面將在如下方簡單的介紹一下ACE_Message_Block相關功能。

  1. 創建消息塊
  2. 釋放消息塊
  3. 從消息塊中讀寫數據
  4. 數據的拷貝
  5. 其它常用函數

1。創建消息塊

創建消息塊的方式比較靈活,常用的有以下幾種方式 :

1。直接給消息塊分配內存空間創建。

     ACE_Message_Block *mb = new ACE_Message_Block (30);

2。共享底層數據塊創建。

    char buffer[100];
     ACE_Message_Block *mb = new ACE_Message_Block (buffer,30);

這種方式共享底層的數據塊,被創建的消息塊並不拷貝該數據,也不假定自己擁有它的所有權。在消息塊mb被銷燬時,相關聯的數據緩衝區data將不會被銷燬。這是有意義的:消息塊沒有拷貝數據,因此內存也不是它分配的,這樣它也不應該負責銷燬它。

3。通過duplicate()函數從已有的消息塊中創建副本。

     ACE_Message_Block *mb = new ACE_Message_Block (30);
     ACE_Message_Block *mb2 = mb->duplicate();

這種方式下,mb2和mb共享同一數據空間,使用的是ACE_Message_Block的引用計數機制。它返回指向要被複制的消息塊的指針,並在內部增加內部引用計數。

4。通過clone()函數從已有的消息塊中複製。

     ACE_Message_Block *mb = new ACE_Message_Block (30);
     ACE_Message_Block *mb2 = mb->clone();

clone()方法實際地創建整個消息塊的新副本,包括它的數據塊和附加部分;也就是說,這是一次"深拷貝"。

2。釋放消息塊

一旦使用完消息塊,程序員可以調用它的release()方法來釋放它。

  1. 如果消息數據內存是由該消息塊分配的,調用release()方法就也會釋放此內存。
  2. 如果消息塊是引用計數的,release()就會減少計數,直到到達0爲止;之後消息塊和與它相關聯的數據塊才從內存中被移除。
  3. 如果消息塊是通過共享已分配的底層數據塊創建的,底層數據塊不會被釋放。

無論消息塊是哪種方式創建的,只要在使用完後及時調用release()函數,就能確保相應的內存能正確的釋放。

3。從消息塊中讀寫數據

ACE_Message_Block提供了兩個指針函數以供程序員進行讀寫操作,rd_ptr()指向可讀的數據塊地址,wr_ptr()指向可寫的數據塊地址,默認情況下都執行數據塊的首地址。下面的例子簡單了演示它的使用方法。

#include "ace/Message_Queue.h"
#include "ace/OS.h"

int main(int argc, char *argv[]) 
{
     ACE_Message_Block *mb = new ACE_Message_Block (30);
     ACE_OS::sprintf(mb->wr_ptr(),"%s","hello");
     ACE_OS::printf("%s\n",mb->rd_ptr ());
     mb->release();
    return 0; 
}

注意:這兩個指針所指向的位置並不會自動移動,在上面的例子中,函數執行完畢後,執行的位置仍然是最開始的0,而不是最新的可寫位置5,程序員需要通過wr_ptr(5)函數手動移動寫指針的位置。

4。數據的拷貝

一般的數據的拷貝可以通過函數來實現數據的拷貝,copy()還會保證wr_ptr()的更新,使其指向緩衝區的新末尾處。

下面的例子演示了copy()函數的用法。

     mb->copy("hello");
     mb->copy("123",4);

注意:由於c++是以'\0'作爲字符串結束標誌的,對於上面的例子,底層數據塊中保存的是"hello\0123\0",而用ACE_OS::printf("%s\n",mb->rd_ptr ());打印出來的結果是"hello",使用copy函數進行字符串連接的時候需要注意。

5。其它常用函數

  1. length()     返回當前的數據長度
  2. next()     獲取和設置下一個ACE_Message_Block的鏈接。(用來建立消息隊列非常有用)
  3. space()     獲取剩餘可用空間大小
  4. size()     獲取和設置數據存儲空間大小。

ACE_Message_Block是ACE中一個很核心的基礎類,應用非常廣泛,所以需要花點心思去研究。學習的過程有幾點心得體會記錄下吧。

        創建消息的這四點引自http://hi.baidu.com/zoupng/blog/item/562290878b31c52ec75cc35c.html

1、直接給消息塊分配內存空間創建。

     ACE_Message_Block *mb = new ACE_Message_Block (30);

2、共享底層數據塊創建。

    char buffer[100];

     ACE_Message_Block *mb = new ACE_Message_Block (buffer,30);

這種方式共享底層的數據塊,被創建的消息塊並不拷貝該數據,也不假定自己擁有它的所有權。在消息塊mb被銷燬時,相關聯的數據緩衝區data將不會被銷燬。這是有意義的:消息塊沒有拷貝數據,因此內存也不是它分配的,這樣它也不應該負責銷燬它。

3、通過duplicate()函數從已有的消息塊中創建副本。

     ACE_Message_Block *mb = new ACE_Message_Block (30);

     ACE_Message_Block *mb2 = mb->duplicate();

這種方式下,mb2和mb共享同一數據空間,使用的是ACE_Message_Block的引用計數機制。它返回指向要被複制的消息塊的指針,並在內部增加內部引用計數。

4、通過clone()函數從已有的消息塊中複製。

     ACE_Message_Block *mb = new ACE_Message_Block (30);

     ACE_Message_Block *mb2 = mb->clone();

clone()方法實際地創建整個消息塊的新副本,包括它的數據塊和附加部分;也就是說,這是一次"深拷貝"。

(第五點引自http://blog.csdn.net/ydogg/archive/2007/10/10/1818949.aspx)

5、ACE_Data_Block一個很奇怪的地方就是ACE_Data_Block::duplicate()的實現, 並沒有創建新的拷貝, 而僅僅是返回了自身(return this). 這中實現方式帶來了很多奇怪的問題.如下面的2,3。
 release()-> release_no_delete()->release_i()->~ACE_Data_Block()

 如果在棧上構造ACE_Data_Block,那麼不能使用release()函數, 因爲release()函數會試圖刪除this。

 如果在棧上構造ACE_Data_Block, 那麼不能使用duplicate()函數, 因爲duplicate()返回的是this指針, 棧中的ACE_Data_Block析構後會導致問題。

 如果在heap上構造ACE_Data_Block,那麼儘量使用release()來替代delete, 如果存在因爲析構並不處理reference count, delete時不考慮其它會導致指針懸空。

理解ACE_Message_Block是用ACE_Data_Block來保存數據的,這一點很關鍵。

補充點吧:

1、ACE_Message_Block的幾個大小,讓人頭暈,下面這張圖非常清晰地顯示了它們的計算方法,如果使用wr_ptr往消息中寫了數據,如果自己不挪動指針的話,length的大小是不會改變的,length的大小等於wr_ptr指針的位置減去rd_ptr的位置。

 ACE_Message_Block - 絢麗也塵埃 - 處女地

2、使用cont方法可以將消息簡單地連接起來,比使用消息隊列要簡單。

給出一段示例代碼吧,這裏沒有涉及消息的到期時間和執行之間等,這些特性需要結合動態隊列來學習使用。

 ACE_Message_Block* mb = new ACE_Message_Block(30);

 ACE_DEBUG((LM_DEBUG, "capacity : %d, length : %d, space : %d\n", mb->capacity(), mb->length(), mb->space()));

 //獲得寫指針
 char* ptr = mb->wr_ptr();

 //將數據寫入消息中
 ACE_OS::memcpy(ptr, "fzjfzjfzj", 3);

 //挪動寫指針
 mb->wr_ptr(9);

 //測試下引用計數,duplicate是淺拷貝,這樣mb2和mb就指向同一個消息了
 ACE_Message_Block *mb2 = mb->duplicate();

 //clone是深度拷貝,不會增加引用計數
 ACE_Message_Block *mb3 = mb->clone();

 //釋放下試試
 //mb2->release();

 //將讀指針往前挪動四個位置會導致length=5
 //mb->rd_ptr(4);

 ACE_DEBUG((LM_DEBUG, "capacity : %d, length : %d, space : %d, reference_count : %d\n", mb->capacity(), mb->length(), mb->space(),  mb->reference_count()));

 //再構造一個消息,用於和mb串聯起來
 ACE_Message_Block* mb4 = new ACE_Message_Block(30);

 ptr = mb4->wr_ptr();

 ACE_OS::memcpy(ptr, "wxy", 3);

 //將mb和mb4串聯起來
 mb->cont(mb4);

 //我期待這樣導致循環鏈表,結果是程序卡死不動了,這裏需要分析下cont的源代碼
 //mb4->cont(mb);

 ACE_Message_Block* pMessageBlock = mb;

 //遍歷消息
 for(; pMessageBlock != NULL; pMessageBlock = mb->cont())
 {
  //注意這個total_capacity的值哦
  ACE_DEBUG((LM_DEBUG, "data: %s, total_length: %d\n", pMessageBlock->rd_ptr(), pMessageBlock->total_capacity()));
  mb = pMessageBlock;
 }

 //在棧中構造一個消息,調用其release方法會導致斷言失敗
 //這是因爲使用delete釋放棧空間
 //ACE_Message_Block mb5 (30);

 //ptr = mb5.wr_ptr();

 //ACE_OS::memcpy(ptr, "wyy", 3);

 //mb5.release();

 

//使用copy的時候需要注意每次copy之後會在字符串後面添加一個'\0'

//下面的例子會存入"hello\0world"

ACE_Message_Block mb6 (30);

mb6.copy("hello");

mb6.copy("world");

//如果這樣做的話就只會hello了

//mb6.copy("hello\0world");


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章