使用ACE_CDR類進行網絡編解碼(5-3)

這次我們來處理邊界調整的問題。留意下面的代碼片段:

  1. char  buf[6] = {0};  
  2. ACE_OutputCDR ocdr(buf, 6, ACE_CDR::BYTE_ORDER_BIG_ENDIAN);  
  3. ACE_CDR::ULong temp1 = 88;  
  4. ACE_CDR::UShort temp2 = 66;  
  5. ocdr << temp1;  
  6. ocdr << temp2;  
  7. int  result = peer.sendn(buf, 6);  
char buf[6] = {0};
ACE_OutputCDR ocdr(buf, 6, ACE_CDR::BYTE_ORDER_BIG_ENDIAN);
ACE_CDR::ULong temp1 = 88;
ACE_CDR::UShort temp2 = 66;
ocdr << temp1;
ocdr << temp2;
int result = peer.sendn(buf, 6);

 

上面的代碼將兩個數據壓到buf中。這裏面有一個隱藏的BUG。第二行用一個buf來構造ACE_OutputCDR對象 時,ACE_OutputCDRr構造函數會進行一個複雜的操作。它先用這個buf構造一個ACE_Message_Block,然後對這個 ACE_Message_Block調用ACE_CDR::mb_align方法,進行一次邊界調整。

如果剛好buf的起點不在4字節的邊界上(不能整除4),則會將ACE_Message_Block的起點後移到4字節對齊的邊界上。這會造成兩個 可能的後果。如果調整的確發生了(假如往後調整了2個字節),那麼上述代碼最後一行發送的內容,實際上就是錯誤的,因爲錯開了2個字節。更爲嚴重的錯誤是 我們的buf剛好是6個字節,我們也寫入了6個字節,但是如果ACE_OutputCDR替我們做了一次調整的話,在寫入的時候就會越界,破壞堆棧(覆寫 buf數組後面的兩個字節)。

解決的方式有兩個。如果是像上述的代碼一樣,用CDR類來對原始的buffer進行處理,那麼可以通過在config.h文件中定義下例的宏來屏蔽對齊行爲。

  1. #define ACE_CDR_IGNORE_ALIGNMENT   
#define ACE_CDR_IGNORE_ALIGNMENT

注意要重新編譯ACE。

另外一種方法是,如果是ACE在項目中用得比較普及的話,建議不要直接用原始buffer,改用ACE_Message_Block。實際上對齊時 是調整了內部的ACE_Message_Block的base指針。如果總是通過ACE_Message_Block的base方法來得到實際 buffer的起始,就不用擔心會發生錯位。這裏唯一要注意的就是要爲可能的調整留出空間,避免上面說的溢出。比如下面的代碼:

  1. ACE_Message_Block mb(1024 + ACE_CDR::MAX_ALIGNMENT);  
ACE_Message_Block mb(1024 + ACE_CDR::MAX_ALIGNMENT);

我們需要一個1024大小的buffer,但是在實際申請空間時加一個冗餘值 ,對齊最大也不可能超過這個冗餘,這樣就避免了壓入數據時引起越界。

ACE進行一次對齊的原因是爲了加快內存操作。結合前面“緊縮”部分的描述,我們可以知道,在缺省情況下,ACE在編解碼時不但將Buffer的起 始外進行對齊處理,裏面的數據類型不論大小也是按4字節對齊的。有興趣的可以看一下MAX_ALIGNMENT這個冗餘量的值是8而不是4,我猜可能是爲 了兼容64位機器

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