<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Thrift之TProtocol類體系原理及源碼詳細解析之緊湊協議類TCompactProtocolT(TCompactProtocol)

      我的新浪微博:http://weibo.com/freshairbrucewoo

      歡迎大家相互交流,共同提高技術。

       

      這個協議類采用了zigzag 編碼,這種編碼是基于Variable-length quantity編碼提出來的,因為Variable-length quantity編碼對于負數的編碼都需要很長的字節數,而zigzag 編碼對于絕對值小的數字,無論正負都可以采用較少的字節來表示,充分利用了 Varint技術。所以這個協議類采用zigzag 編碼可以節省傳輸空間,使數據的傳輸效率更高。至于zigzag具體的編碼實現方式可以網上查查,其實就是把從低位到最后一個還存在1(二進制)的最高位表示出來就可以了。這個協議類對外提供的方法和上面介紹的二進制協議相同,這樣可以很方便使用者從一種協議改變到另一種協議。

      下面我同樣結合scribe提供的Log方法來分析這個協議類的功能,不過不會像上面二進制協議在把整個過程分析了,我只會分析與協議相關的部分了,分析一些比較難懂的一些函數功能,分析的思路還是按照函數調用過程來分析。

      首先還是分析writeMessageBegin函數,下面是這個函數的實現代碼:

       1 template <class Transport_> uint32_t TCompactProtocolT<Transport_>::writeMessageBegin(
       2 
       3     const std::string& name, const TMessageType messageType, const int32_t seqid) {
       4 
       5   uint32_t wsize = 0;
       6 
       7   wsize += writeByte(PROTOCOL_ID);//寫入這個協議的產品ID號:為0x82
       8 
       9   wsize += writeByte((VERSION_N & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK));//寫入此協議的版本號和消息類型:前3位是消息類型,后面5位是協議版本號
      10 
      11   wsize += writeVarint32(seqid);//寫入請求序列號
      12 
      13   wsize += writeString(name);//寫入消息名稱(也就是函數調用名稱)
      14 
      15   return wsize;//返回寫入的大小,多少字節
      16 
      17 }

       

      因為這些協議類都是模板類,所以每一個函數也就是模板函數了。函數具體的功能代碼里有詳細注釋,其中的writeByte函數就是寫入一個字節到服務器。這里與二進制協議不同的是這里寫入請求序列號(也就是對于所有的整型數)都調用的writeVarint32函數,這個函數就是采用zigzag編碼寫入整型數到服務器,代碼如下:

       1 template <class Transport_> uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
       2 
       3   uint8_t buf[5];//對于一個整數,zigzag編碼最大采用5個字節保存
       4 
       5   uint32_t wsize = 0;
       6 
       7   while (true) {
       8 
       9     if ((n & ~0x7F) == 0) {//判斷除了最低7位是否還有其他高位為1(二進制)
      10 
      11       buf[wsize++] = (int8_t)n;//沒有了代表著就是最后一個字節
      12 
      13       break;//退出循環
      14 
      15     } else {
      16 
      17       buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);//取最低7位加上第8位(為1代表后續還有字節屬于這個整數,為0代表這是這個整數的最后一個字節了。
      18 
      19       n >>= 7;//移走已經編碼的位數
      20 
      21     }
      22 
      23   }
      24 
      25   trans_->write(buf, wsize);//寫入編碼的字節數
      26 
      27   return wsize;//返回寫入的字節數
      28 
      29 }

       

      這個函數的功能就是對整數進行Variable-length quantity編碼后寫入,如果為負數需要處理。如果不處理那么每一個負數都需要5個字節來編碼,因為最高位表示符號位,而負數的符號位用1表示(也就是說負數的最高位永遠為1)。處理的方式也很簡單(就是zigzag編碼),就是把最高位(符號位)移動到最低位,最低位到次高位一次向高位移動一位,代碼如下(就一句就實現了):

      1 template <class Transport_>
      2 
      3 uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
      4 
      5   return (n << 1) ^ (n >> 31);
      6 
      7 }

       

      上面寫入整數和處理負數都是針對的32位的,當然也有64位的相應函數,實現方式相同。我們在回到writeMessageBegin函數,里面還有一個writeString函數用來寫入一個字符串的,與二進制不同的是寫入字符串長度也是采用了可變長度編碼的方式寫入,然后寫入字符串的具體數據,它是調用另一個函數writeBinary寫入,writeBinary實現代碼如下:

       1 template <class Transport_>
       2 
       3 uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) {
       4 
       5   uint32_t ssize = str.size();
       6 
       7   uint32_t wsize = writeVarint32(ssize) + ssize;//寫入字符串的長度并計算寫入的長度(包括字符串的長度)
       8 
       9   trans_->write((uint8_t*)str.data(), ssize);//寫入字符串的數據
      10 
      11   return wsize;
      12 
      13 }

       

      寫消息函數分析完畢以后我們在來看看對應的讀消息函數readMessageBegin,看這個函數必須和寫入消息的函數對應起來看,不然就不能理解它讀取和處理的流程代碼,具體實現如下代碼:

       1 template <class Transport_> uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
       2 
       3     std::string& name, TMessageType& messageType, int32_t& seqid) {
       4 
       5   uint32_t rsize = 0;
       6 
       7   int8_t protocolId;
       8 
       9   int8_t versionAndType;
      10 
      11   int8_t version;
      12 
      13   rsize += readByte(protocolId);//讀取協議產品ID號
      14 
      15   if (protocolId != PROTOCOL_ID) {//判斷是不是這個協議的產品ID號,不是就拋出異常
      16 
      17     throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
      18 
      19   }
      20 
      21   rsize += readByte(versionAndType);//讀取此協議的版本號和消息類型
      22 
      23   version = (int8_t)(versionAndType & VERSION_MASK);//取出協議版本號
      24 
      25   if (version != VERSION_N) {//判斷是不是對應的協議版本號,不是拋出異常
      26 
      27     throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
      28 
      29   }
      30 
      31   messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03);//取出消息類型
      32 
      33   rsize += readVarint32(seqid);//讀取請求序列號
      34 
      35   rsize += readString(name);//讀取消息名稱(函數名稱)
      36 
      37   return rsize;//返回讀取的長度(字節)
      38 
      39 }

       

      通過對照寫入消息的函數就很容易理解,因為你寫入什么我就讀什么并且判斷是不是相同協議寫入的,具體分析可以看上面的代碼和詳細的注釋。而且還有一點就是具體的寫入數據類型的函數也是采用對應類型的讀函數,例如讀可變長整型寫入就是采用可變長讀函數readVarint32,寫字符串對應讀字符串函數readString,對照相應的寫入函數來看這些讀數據函數就非常好理解了,就不具體分析這些讀函數了。

      下面在分析幾個復合數據類型的寫入函數,因為這些寫入函數存在一定技巧不容易(或者說不那么直觀吧)理解清楚。首先看看struct類型的數據寫入的過程,它分為寫入開始、中間處理和寫入結束。下面是開始寫入struct的代碼:

       1 template <class Transport_>
       2 
       3 uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) {
       4 
       5   (void) name;
       6 
       7   lastField_.push(lastFieldId_);//把最后寫入的字段ID壓入堆棧
       8 
       9   lastFieldId_ = 0;//重新設置為0
      10 
      11   return 0;
      12 
      13 }

       

      這開始寫入的函數沒有做什么具體的工作,只是把最后寫入的字段ID壓入堆棧,這樣做的目的是處理那種struct嵌套的數據結構類型。

      Struct里面的是一個一個的字段,所以根據struct的字段個數分別調用字段寫入函數依次寫入,字段寫入函數定義如下:

       1 template <class Transport_> int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
       2 
       3     const char* name, const TType fieldType, const int16_t fieldId, int8_t typeOverride) {
       4 
       5   (void) name;//為了防止編譯器產生警告信息
       6 
       7   uint32_t wsize = 0;
       8 
       9   // 如果存在對于對應的類型就轉換為對應的
      10 
      11   int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);
      12 
      13   // 檢查字段ID是否使用了增量編碼
      14 
      15   if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {//如果使用了增量編碼并增量且小于等于15
      16 
      17     wsize += writeByte((fieldId - lastFieldId_) << 4 | typeToWrite);//字段ID和數據類型一起寫入
      18 
      19   } else {//否則單獨寫入
      20 
      21     wsize += writeByte(typeToWrite);//寫入數據類型
      22 
      23     wsize += writeI16(fieldId);//寫入字段ID
      24 
      25   }
      26 
      27   lastFieldId_ = fieldId;//保存寫入字段ID為最后一個寫入的ID
      28 
      29   return wsize;//返回寫入的長度
      30 
      31 }

       

      當結構體里面的每一個字段都寫入以后還需要調用writeStructEnd函數來處理結束一個struct的寫入,主要處理是字段ID的相關內容,實現代碼如下:

      1 template <class Transport_> uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
      2 
      3   lastFieldId_ = lastField_.top();//取得最后一次壓入堆棧的字段ID號
      4 
      5   lastField_.pop();//彈出以取得的字段ID
      6 
      7   return 0;
      8 
      9 }

       

      同樣的結構體也有對應的讀取函數,具體實現就不在具體分析了!下面繼續分析一些特殊的處理代碼,首先看看負數在進行zigzag編碼前怎樣處理,對于32位和64位都是一句代碼就搞定,如下代碼:

      1 return (n >> 1) ^ -(n & 1);

       

      這句代碼的作用就是把最高位的符號位移動到最低位,然后最低位到次高位依次向高位移動一位,這樣就避免了所有負數都需要最長的字節來編碼。在看看讀可變長編碼寫入整型數的函數,32位和64位都是相同的實現,因為32位也是調用64位的函數實現的,實現代碼如下:

       1 template <class Transport_> uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
       2 
       3   uint32_t rsize = 0;
       4 
       5   uint64_t val = 0;
       6 
       7   int shift = 0;
       8 
       9   uint8_t buf[10];  // 64 位采用zigzag編碼最長可能是10字節
      10 
      11   uint32_t buf_size = sizeof(buf);
      12 
      13   const uint8_t* borrowed = trans_->borrow(buf, &buf_size);//并不是所有transport都支持 
      14 
      15   if (borrowed != NULL) {// 快路徑,要讀的數據已經在緩存中
      16 
      17     while (true) {
      18 
      19       uint8_t byte = borrowed[rsize];
      20 
      21       rsize++;
      22 
      23       val |= (uint64_t)(byte & 0x7f) << shift;//取得對應編碼數據的7位
      24 
      25       shift += 7;//后7位
      26 
      27       if (!(byte & 0x80)) {//是否還有屬于這個數的編碼字節,字節的最高位表示:0表示沒有了
      28 
      29         i64 = val;//讀取解碼后的真正有效值
      30 
      31         trans_->consume(rsize);//消耗了多少字節,即表示這個編碼用了多少字節
      32 
      33         return rsize;
      34 
      35       }
      36 
      37       // 檢查編碼數據是否超過了最長限制,是就拋出一個無效的異常
      38 
      39       if (UNLIKELY(rsize == sizeof(buf))) {
      40 
      41         throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
      42 
      43       }
      44 
      45     }
      46 
      47   } 
      48 
      49   else {// 慢路徑,要讀的數據還沒有存在緩存中
      50 
      51     while (true) {
      52 
      53       uint8_t byte;
      54 
      55       rsize += trans_->readAll(&byte, 1);//讀取一個字節
      56 
      57       val |= (uint64_t)(byte & 0x7f) << shift;//取得7位的編碼數據
      58 
      59       shift += 7;
      60 
      61       if (!(byte & 0x80)) {
      62 
      63         i64 = val;
      64 
      65         return rsize;
      66 
      67       } 
      68 
      69       if (UNLIKELY(rsize >= sizeof(buf))) {//同樣檢查數據的有效性:最大字節長度不超過10個字節
      70 
      71         throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
      72 
      73       }
      74 
      75     }
      76 
      77   }
      78 
      79 }

       

      由于采用了可變長度編碼的原因,所以不知道一次性應該讀取多少個字節是一個完整的數據。為了讀取效率所以一次性直接讀取最長可能的字節數量,也就是10字節,因為64位最長的可變長編碼就是10字節長,然后根據實際消耗的字節數從讀取元跳過已經消耗的字節數。不過底層的傳輸層,有些協議可能不支持這種預讀取方式,所以就只有一個字節一個字節的讀取。

      這個協議最大的特點就是采用了可變長度編碼,并且采用zigzag編碼處理負數總是需要采用最長的編碼字節的問題,所以相對于比較二進制而言效率提高了不少。

      posted @ 2012-06-11 23:49  薔薇理想人生  閱讀(4457)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 国产永久免费高清在线观看| 欧洲亚洲色一区二区色99| 亚洲综合不卡一区二区三区| 欧美日本在线一区二区三区| 公天天吃我奶躁我的在线观看| 成人无码一区二区三区网站| 伊人久久大香线蕉成人| 日韩激情一区二区三区| 欧美亚洲另类制服卡通动漫 | 澄城县| 精品国产乱码久久久久app下载 | 免费可以在线看a∨网站| 亚洲熟女乱综合一区二区三区| 欧美乱妇高清无乱码免费| 国产稚嫩高中生呻吟激情在线视频| 亚洲一区二区精品偷拍| 成人无码一区二区三区网站| 欧美www在线观看| 久青草视频在线观看免费| 男人扒开女人内裤强吻桶进去| 99久久精品看国产一区| 日韩熟妇中文色在线视频| 97亚洲熟妇自偷自拍另类图片| 日韩精品中文字一区二区| 中文字幕国产精品日韩| 久久久久无码精品国产不卡| 一区二区三区午夜无码视频| 无码人妻精品一区二区三区下载| 人妻内射视频麻豆| 成av人片一区二区久久| 亚洲精品国产免费av| 社会| 日韩人妻精品中文字幕专区| 国产在线午夜不卡精品影院 | 久热这里有精品免费视频| 国产91精品调教在线播放| 2018年亚洲欧美在线v| 龙泉市| 99久久er热在这里只有精品99 | 国产一区二区三区粉嫩av| 国产成人无码免费视频在线 |