先上张图,说明一下thrift的二进制协议是什么东东。

报文格式编码:

bool类型:

  一个字节的类型,两个字节的字段编号,一个字节的值(true:1,false:0).

Byte型:

  一个字节的类型,两个字节的字段编号,一个字节的值.

I16型:

  一个字节的类型,两个字节的字段编号,两个字节的值.

I32型:

  一个字节的类型,两个字节的字段编号,四个字节的值.

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

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.

Thrift的TBinaryProtocol二进制协议分析的更多相关文章

  1. Thrift的TCompactProtocol紧凑型二进制协议分析

    Thrift的紧凑型传输协议分析: 用一张图说明一下Thrift的TCompactProtocol中各个数据类型是怎么表示的. 报文格式编码: bool类型: 一个字节. 如果bool型的字段是结构体 ...

  2. Thrift的TJsonProtocol协议分析

    Thrift协议实现目前有二进制协议(TBinaryProtocol),紧凑型二进制协议(TCompactProtocol)和Json协议(TJsonProtocol). 前面的两篇文字从编码和协议原 ...

  3. REST RPC HTTP vs 高性能二进制协议 序列化和通信协议

    edisonchou https://mp.weixin.qq.com/s/-XZXqXawR-NxJMPCeiNsmg .NET Core微服务之服务间的调用方式(REST and RPC) Edi ...

  4. Google的Protobuf协议分析

    protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...

  5. Thrift笔记(三)--Thrift框架通信源码分析

    Thrift 客户端调用RPC的Demo public static void main(String[] args) throws Exception { TTransport transport ...

  6. 协议分析TMP

    最近闲来有事, 分析了一个非常低端(非常低端的意思是说你不应该对她是否能取代你现有的QQ客户端作任何可能的奢望,她只是一个实验性的东西)的手机QQ的协议, 是手机QQ3.0,      所用到的TCP ...

  7. C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

    原文地址:http://blog.csdn.net/wuyazhe/article/details/5627253 我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要 ...

  8. [转载] TLS协议分析 与 现代加密通信协议设计

    https://blog.helong.info/blog/2015/09/06/tls-protocol-analysis-and-crypto-protocol-design/?from=time ...

  9. MySQL协议分析

    MySQL协议分析 标签: mysql 2015-02-27 10:22 1807人阅读 评论(1) 收藏 举报  分类: 数据库(19)    目录(?)[+]   1 交互过程 MySQL客户端与 ...

随机推荐

  1. python爬虫学习(8) —— 关于4399的一个小Demo

    堂弟喜欢各种游戏,在没有网络的情况下,上4399显得很无力. 另外,4399广告好多,,而且加载慢.. 怎么办,,写个爬虫吧,,把4399上的"好玩"游戏爬下来. 1. 分析阶段 ...

  2. S5PV210_时钟系统

    1.S5PV210的时钟获得:外部晶振+内部时钟发生器+内部PLL产生高频时钟+内部分频器分频 S5PV210外部有4个W晶振接口,可以根据需要来决定在哪里接晶振.接了晶振之后上电相应的模块就能产生振 ...

  3. Java Web中的中文编码

    Java Web开发中经常会遇到中文编码问题,那么为什么需要编码呢?因为人类需要表示的符号太多,无法用1个字节来表示,而计算机中存储信息最小单元为1个字节.所以必须指定char与byte之间的编码规则 ...

  4. 00 Cadence学习总目录

    这个系列是我学习于博士CADENCE视频教程60讲时,一边学一边记的笔记.使用的CADENCE16.6. 01-03课 了解软件 创建工程 创建元件库 分裂元件的制作方法 04课 正确使用hetero ...

  5. LZ77.py

    import math from bitarray import bitarray class LZ77Compressor: """ A simplified impl ...

  6. [LeetCode] Implement Queue using Stacks 用栈来实现队列

    Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of ...

  7. OpenCV安装与配置

    本文使用OpenCV2.48在win10平台下操作. 一,关于OpenCV OpenCV是开源C++计算机可视化处理库,它集成了很多计算机图形图像处理的功能.还有机器学习,字符识别,人脸识别,物体检测 ...

  8. java 上传图片

    1.导入smartupload.jar包 ,添加uploadIMG.jsp,upfileIMG.jsp. 2.需要在项目下面建立一个保存文件的文件夹pic或者upload 3.在调用的地方调用子框架u ...

  9. #define与typedef在重定义类型中的区别

    #define 为完全的替换 typedef 重新定一个数据类型 eg #define charp1 char* typedef char* charp2charp1 a,b; //a char* b ...

  10. Spring缓存机制的理解

    在spring缓存机制中,包括了两个方面的缓存操作:1.缓存某个方法返回的结果:2.在某个方法执行前或后清空缓存. 下面写两个类来模拟Spring的缓存机制: package com.sin90lzc ...