ACE_Message_Block在Ace中用來表示消息的存放空間,可用做網絡通信中的消息緩衝區,使用非常頻繁,下面將在如下方簡單的介紹一下ACE_Message_Block相關功能。
- 創建消息塊
- 釋放消息塊
- 從消息塊中讀寫數據
- 數據的拷貝
- 其它常用函數
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()方法來釋放它。
- 如果消息數據內存是由該消息塊分配的,調用release()方法就也會釋放此內存。
- 如果消息塊是引用計數的,release()就會減少計數,直到到達0爲止;之後消息塊和與它相關聯的數據塊才從內存中被移除。
- 如果消息塊是通過共享已分配的底層數據塊創建的,底層數據塊不會被釋放。
無論消息塊是哪種方式創建的,只要在使用完後及時調用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。其它常用函數
- length() 返回當前的數據長度
- next() 獲取和設置下一個ACE_Message_Block的鏈接。(用來建立消息隊列非常有用)
- space() 獲取剩餘可用空間大小
- 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的位置。
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");