Thrift的TBinaryProtocol二進制協議分析

先上張圖,說明一下thrift的二進制協議是什麼東東。git

報文格式編碼:github

bool類型:socket

  一個字節的類型,兩個字節的字段編號,一個字節的值(true:1,false:0).函數

Byte型:測試

  一個字節的類型,兩個字節的字段編號,一個字節的值.ui

I16型:編碼

  一個字節的類型,兩個字節的字段編號,兩個字節的值.lua

I32型:spa

  一個字節的類型,兩個字節的字段編號,四個字節的值.blog

I64型和double型:

  一個字節的類型,兩個字節的字段編號,八個字節的值.

String型:

  一個字節的類型,兩個字節的字段編號,四個字節的負載數據長度,負載數據的值.

Struct型:

  一個字節的類型,兩個字節的字段編號,結構體負載數據,一個字節的結束標記.

MAP型:

  一個字節的類型,兩個字節的字段編號,一個字節的鍵類型,一個字節的值類型,四個字節的負載數據長度,負載數據的值.

Set型:

  一個字節的類型,兩個字節的字段編號,一個字節的值類型,四個字節的負載數據長度,負載數據的值.

List型:

  一個字節的類型,兩個字節的字段編號,一個字節的值類型,四個字節的負載數據長度,負載數據的值.

消息(函數)型:

  表示方式一:四個字節的版本(含調用類型),四個字節的消息名稱長度,消息名稱,四個字節的流水號,消息負載數據的值,一個字節的結束標記。

  表示方式二:四個字節的消息名稱長度,消息名稱,一個字節調用類型,四個字節的流水號,消息負載數據的值,一個字節的結束標記。

  對嚴格的thrift消息,必須包含32爲版本信息。

  如有32爲版本信息,函數調用(請求:1,響應:2,異常:3,無返回值的請求:4)被包含到32爲版本中,不獨立出現。

  計算方法:

  32位版本 = 0x8001000 & 函數調用

  計算後,請求報文的32爲版本值爲 0x80010001;響應報文的32爲版本值爲 0x80010002;異常報文的32爲版本值爲 0x80010003;

  若沒有32爲版本信息時,函數調用(請求:1,響應:2,異常:3,無返回值的請求:4)獨立出如今消息報文中。

thrift的IDL文件以下:

struct ArgStruct {
    1:byte argByte,
    2:string argString
    3:i16  argI16,
    4:i32  argI32,
    5:i64  argI64,
    6:double argDouble,
    
}

service RpcService {
    list<string> funCall(
        1:ArgStruct argStruct,
        2:byte argByte,
        3:i16  argI16,
        4:i32  argI32,
        5:i64  argI64,
        6:double argDouble,
        7:string argString,
        8:map<string, string> paramMapStrStr,
        9:map<i32, string> paramMapI32Str,
        10:set<string> paramSetStr,
        11:set<i64> paramSetI64,
        12:list<string> paramListStr,
        ),
}

生成lua代碼

thrift -gen lua rpcbin.thrift

 

寫一個小的測試例子客戶端(lua):

 

require "rpcbin_RpcService"
require "TFramedTransport"
require "TBinaryProtocol"
require "TSocket"

function demoFunc()
    local socket = TSocket:new{
        host='127.0.0.1',
        port=8090
    }
    local protocol = TBinaryProtocol:new{
        trans = socket
    }
    client = RpcServiceClient:new{
        protocol = protocol
    }
    local argStruct = ArgStruct:new{
      argByte = 53,
      argString = "str value",
      argI16 = 54,
      argI32 = 654321,
      argI64 = 334455,
      argDouble = 4334.55
    }
    -- Open the socket  
    socket:open()
    pmap = {}
    pmap.name = "namess"
    pmap.pass = "vpass"
    pistrmap = {}
    pistrmap[2] = "str2"
    pistrmap[3] = "str3"
    ret = client:funCall(argStruct, 65, 2533, 4455,
        98765, 3.2212, "login", pmap,
        pistrmap,
        {"ele1", "ele2", "ele3"},
        {1,2,3,4},
        {"l1","l2","l3"});
    for k,v in pairs(ret)
    do
        print(k, v)
    end
    socket:close()
end
demoFunc()

 

一些依賴文件代碼

Thrift.lua:

TType = {
  STOP   = 0,
  VOID   = 1,
  BOOL   = 2,
  BYTE   = 3,
  I08    = 3,
  DOUBLE = 4,
  I16    = 6,
  I32    = 8,
  I64    = 10,
  STRING = 11,
  UTF7   = 11,
  STRUCT = 12,
  MAP    = 13,
  SET    = 14,
  LIST   = 15,
  UTF8   = 16,
  UTF16  = 17
}

TMessageType = {
  CALL  = 1,
  REPLY = 2,
  EXCEPTION = 3,
  ONEWAY = 4
}
rpcbin_RpcService.lua line:298-321

  if self.paramSetStr then
    oprot:writeFieldBegin('paramSetStr', TType.SET, 10)
    oprot:writeSetBegin(TType.STRING, ttable_size(self.paramSetStr))
    for _,iter31 in pairs(self.paramSetStr) do
      oprot:writeString(iter31)
    end
    oprot:writeSetEnd()
    oprot:writeFieldEnd()
  end
  if self.paramSetI64 then
    oprot:writeFieldBegin('paramSetI64', TType.SET, 11)
    oprot:writeSetBegin(TType.I64, ttable_size(self.paramSetI64))
    for _,iter32 in pairs(self.paramSetI64) do
      oprot:writeI64(iter32)
    end
    oprot:writeSetEnd()
    oprot:writeFieldEnd()
  end

須要修改一下set和list的取值方式,thrift生成的代碼有個問題(對set和list只寫下標不傳值),因此作以下修改:

for iter31,_ in pairs(self.paramSetStr) do 改爲 for _,iter31 in pairs(self.paramSetStr) do
for iter32,_ in pairs(self.paramSetI64) do 改爲 for _,iter32 in pairs(self.paramSetI64) do
for iter33,_ in ipairs(self.paramListStr) do 改爲  for _,iter33 in ipairs(self.paramListStr) do

  

執行lua cln.lua前抓包看看thrift的二進制協議是什麼樣子?

 

接下來咱們對照上面的圖分析一下這個協議包,看能不能把每一個字節的意義說明白。 

0000 00 00 00 07
0000 66 75 6e 43 61 6c 6c 01 00 00 00 01 0c 00 01 03
0010 00 01 35 0b 00 02 00 00 00 09 73 74 72 20 76 61
0020 6c 75 65 06 00 03 00 36 08 00 04 00 09 fb f1 0a
0030 00 05 00 00 00 00 00 05 1a 77 04 00 06 cd cc cc
0040 cc 8c ee b0 40 00 03 00 02 41 06 00 03 09 e5 08
0050 00 04 00 00 11 67 0a 00 05 00 00 00 00 00 01 81
0060 cd 04 00 06 69 00 6f 81 04 c5 09 40 0b 00 07 00
0070 00 00 05 6c 6f 67 69 6e 0d 00 08 0b 0b 00 00 00
0080 02 00 00 00 04 6e 61 6d 65 00 00 00 06 6e 61 6d
0090 65 73 73 00 00 00 04 70 61 73 73 00 00 00 05 76
00a0 70 61 73 73 0d 00 09 08 0b 00 00 00 02 00 00 00
00b0 02 00 00 00 04 73 74 72 32 00 00 00 03 00 00 00
00c0 04 73 74 72 33 0e 00 0a 0b 00 00 00 03 00 00 00
00d0 04 65 6c 65 31 00 00 00 04 65 6c 65 32 00 00 00
00e0 04 65 6c 65 33 0e 00 0b 0a 00 00 00 04 00 00 00
00f0 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00
0100 00 00 00 00 03 00 00 00 00 00 00 00 04 0f 00 0c
0110 0b 00 00 00 03 00 00 00 02 6c 31 00 00 00 02 6c
0120 32 00 00 00 02 6c 33 00


4 個字節00 00 00 07 表示長度7;
7 個字節66 75 6e 43 61 6c 6c 表示長度7的值funCall;
1 個字節01 表示消息請求 TMessageType.CALL = 1;
4 個字節00 00 00 01 表示請求流水號1;

函數funCall的第一個參數:
3 個字節0c 00 01表示結構體TType.STRUCT = 12,函數funCall的第一個參數的編號是1, 1:ArgStruct argStruct,

struct ArgStruct {
1:byte argByte,
2:string argString
3:i16 argI16,
4:i32 argI32,
5:i64 argI64,
6:double argDouble,

}
結構體分析以下:
結構體第一個元素:
4 個字節03 00 01 35 表示ArgStruct的第一個元素1:byte argByte,
類型爲03,TType.BYTE = 3,元素編號爲1,值爲16進制35,即10進制53;

結構體第二個元素:
16個字節0b 00 02 00 00 00 09 73 74 72 20 76 61 6c 75 65
表示ArgStruct的第二個元素2:string argString,
類型爲0b,TType.STRING = 11,元素編號00 02爲2,
值的長度00 00 00 09爲9,值73 74 72 20 76 61 6c 75 65爲str value;

結構體第三個元素:
5個字節06 00 03 00 36 表示ArgStruct的第三個元素3:i16 argI16,
類型爲06,TType.I16 = 6,元素編號00 03爲3,
值00 36爲54;

結構體第四個元素:
7個字節08 00 04 00 09 fb f1 表示ArgStruct的第四個元素4:i32 argI32,
類型爲08,TType.I32 = 8,元素編號00 04爲4,
值00 09 fb f1爲654321;

結構體第五個元素:
11個字節0a 00 05 00 00 00 00 00 05 1a 77 表示ArgStruct的第五個元素5:i64 argI64,
類型爲0a,TType.I64 = 10,元素編號00 05爲5,
值00 00 00 00 00 05 1a 77爲334455;

結構體第六個元素:
12個字節04 00 06 cd cc cc cc 8c ee b0 40 00 表示ArgStruct的第六個元素6:double argDouble,
類型爲04,TType.DOUBLE = 4,元素編號00 06爲6,
值cd cc cc cc 8c ee b0 40爲4334.55,最後一個00表示結構體結束TType.STOP = 0;

函數funCall的第二個參數:
2:byte argByte,
4個字節03 00 02 41 表示類型爲03,TType.BYTE = 3,參數編號00 02爲2,
值41爲65;


函數funCall的第三個參數:
3:i16 argI16,
5個字節06 00 03 09 e5 表示類型爲TType.I16 = 6,元素編號00 03爲3,
值09 e5爲2533;


函數funCall的第四個參數:
4:i32 argI32,
字節08 00 04 00 00 11 67 表示類型爲08,TType.I32 = 8,元素編號00 04爲4,
值00 00 11 67爲4455;


函數funCall的第五個參數:
5:i64 argI64,
字節0a 00 05 00 00 00 00 00 01 81 cd 表示類型爲0a,TType.I64 = 10,元素編號00 05爲5,
值00 00 00 00 00 01 81 cd爲98765;


函數funCall的第六個參數:
6:double argDouble,
4個字節04 00 06 69 00 6f 81 04 c5 09 40 表示類型爲04,TType.DOUBLE = 4,元素編號00 06爲6,
值69 00 6f 81 04 c5 09 40爲3.2212;


函數funCall的第七個參數:
7:string argString,
4個字節0b 00 07 00 00 00 05 6c 6f 67 69 6e 表示類型爲0b,TType.STRING = 11,元素編號00 07爲7,值的長度00 00 00 05爲5,
值6c 6f 67 69 6e爲login;


函數funCall的第八個參數:
8:map<string, string> paramMapStrStr,
字節0d 00 08 0b 0b 00 00 00 02 00 00 00 04 6e 61 6d 65 00 00 00 06 6e 61 6d 65 73 73 00 00 00 04 70 61 73 73 00 00 00 05 76 70 61 73 73

表示類型爲0d,TType.MAP = 13,元素編號00 08爲8,map的鍵和值的類型0b 0b爲,TType.STRING = 11,
Map裏面有2個元素00 00 00 02爲2,
第一個元素的鍵長度00 00 00 04爲4,鍵的值6e 61 6d 65爲name,
值長度00 00 00 06爲6,值的值6e 61 6d 65 73 73爲namess,
第二個元素的鍵長度00 00 00 04爲4,鍵的值0 61 73 73爲pass,
值長度00 00 00 05爲5,值的值76 70 61 73 73爲vpass;


函數funCall的第九個參數:
9:map<i32, string> paramMapI32Str,
字節0d 00 09 08 0b 00 00 00 02 00 00 00 02 00 00 00 04 73 74 72 32 00 00 00 03 00 00 00 04 73 74 72 33

表示類型爲0d,TType.MAP = 13,元素編號00 09爲9,paramMapI32Str,map的鍵的類型08,TType.I32 = 8;map的值的類型0b爲,TType.STRING = 11,
Map裏面有2個元素00 00 00 02爲2,
第一個元素鍵的值00 00 00 02爲2,
值長度00 00 00 04爲4,值的值73 74 72 32爲str2,
第二個元素鍵的值00 00 00 03爲3,
值長度00 00 00 04爲4,值的值73 74 72 33爲str3;


函數funCall的第十個參數:
10:set<string> paramSetStr,
字節0e 00 0a 0b 00 00 00 03 00 00 00 04 65 6c 65 31 00 00 00 04 65 6c 65 32 00 00 00 04 65 6c 65 33

表示類型爲0e,TType.SET = 14,元素編號00 0a爲10,
set的值的類型0b爲,TType.STRING = 11,
set裏面有3個元素00 00 00 03爲3,
第一個元素值長度00 00 00 04爲4,值65 6c 65 31爲ele1,
第二個元素值長度00 00 00 04爲4,值65 6c 65 32爲ele2,
第三個元素值長度00 00 00 04爲4,值65 6c 65 33爲ele3;


函數funCall的第十一個參數:
11:set<i64> paramSetI64,
字節0e 00 0b 0a 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 04

表示類型爲0e,TType.SET = 14,元素編號00 0b爲11,
set的值的類型0a爲,TType.I64 = 10,
set裏面有4個元素00 00 00 04爲4,
第一個元素值00 00 00 00 00 00 00 01爲1,
第二個元素值00 00 00 00 00 00 00 02爲2,
第三個元素值00 00 00 00 00 00 00 03爲3,
第四個元素值00 00 00 00 00 00 00 04爲4,


函數funCall的第十二個參數:
12:list<string> paramListStr,
字節0f 00 0c 0b 00 00 00 03 00 00 00 02 6c 31 00 00 00 02 6c 32 00 00 00 02 6c 33 00

表示類型爲0f,TType.LIST = 15,元素編號00 0c爲12,
list的值的類型0b爲,TType.STRING = 11,
set裏面有3個元素00 00 00 03爲3,
第一個元素值長度00 00 00 02爲2,值6c 31爲l1,
第二個元素值長度00 00 00 02爲2,值6c 32爲l2,
第三個元素值長度00 00 00 02爲2,值6c 33爲l3;
最後一個00表示結構體結束TType.STOP = 0;

 

demo git:https://github.com/gityf/lua

Done.

相關文章
相關標籤/搜索