Thrift的TBinaryProtocol二进制协议分析
先上张图,说明一下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二进制协议分析的更多相关文章
- Thrift的TCompactProtocol紧凑型二进制协议分析
Thrift的紧凑型传输协议分析: 用一张图说明一下Thrift的TCompactProtocol中各个数据类型是怎么表示的. 报文格式编码: bool类型: 一个字节. 如果bool型的字段是结构体 ...
- Thrift的TJsonProtocol协议分析
Thrift协议实现目前有二进制协议(TBinaryProtocol),紧凑型二进制协议(TCompactProtocol)和Json协议(TJsonProtocol). 前面的两篇文字从编码和协议原 ...
- REST RPC HTTP vs 高性能二进制协议 序列化和通信协议
edisonchou https://mp.weixin.qq.com/s/-XZXqXawR-NxJMPCeiNsmg .NET Core微服务之服务间的调用方式(REST and RPC) Edi ...
- Google的Protobuf协议分析
protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...
- Thrift笔记(三)--Thrift框架通信源码分析
Thrift 客户端调用RPC的Demo public static void main(String[] args) throws Exception { TTransport transport ...
- 协议分析TMP
最近闲来有事, 分析了一个非常低端(非常低端的意思是说你不应该对她是否能取代你现有的QQ客户端作任何可能的奢望,她只是一个实验性的东西)的手机QQ的协议, 是手机QQ3.0, 所用到的TCP ...
- C# 串口操作系列(3) -- 协议篇,二进制协议数据解析
原文地址:http://blog.csdn.net/wuyazhe/article/details/5627253 我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要 ...
- [转载] TLS协议分析 与 现代加密通信协议设计
https://blog.helong.info/blog/2015/09/06/tls-protocol-analysis-and-crypto-protocol-design/?from=time ...
- MySQL协议分析
MySQL协议分析 标签: mysql 2015-02-27 10:22 1807人阅读 评论(1) 收藏 举报 分类: 数据库(19) 目录(?)[+] 1 交互过程 MySQL客户端与 ...
随机推荐
- spring3 循环依赖
循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环.此处不 ...
- Nginx模块之http.md
ngx_http_access_module ngx_http_access_module模块允许限制对某些客户端地址的访问. 访问也可以通过密码,子请求的结果或JWT来限制. 通过地址和密码的同时访 ...
- Opserver简单部署
版权声明:本文为博主原创文章,未经博主允许不得转载. 一.下载opserver项目 地址:https://github.com/opserver/Opserver/ 二.用vs2012及以上版本打卡o ...
- [板子]最小费用最大流(Dijkstra增广)
最小费用最大流板子,没有压行.利用重标号让边权非负,用Dijkstra进行增广,在理论和实际上都比SPFA增广快得多.教程略去.转载请随意. #include <cstdio> #incl ...
- asp.net mvc 绑定客户端post过来的复杂JSON数据
客户端代码如下: <input type="button" id="btnTest" value="测试" /><br / ...
- List [][]
# -*- coding:utf-8 -*- L = [ ['Apple', 'Google', 'Microsoft'], ['Java', 'Python', 'Ruby', ...
- Javascript字节转换
//文件大小转换 function bytesToSize(bytes) { if (bytes === 0) return '0 B'; var k = 1024; sizes = ['B', 'K ...
- Beta版本冲刺第二天
Aruba 408 409 410 428 429 431 完成任务: 分类界面,点击新建弹出输入名字的对话框,确定和取消按钮预留intent 添加通过分享保存到本应用的入口 立会照片: 燃尽图: c ...
- 爬虫框架--webmagic
官方有详细的使用文档:http://webmagic.io/docs/zh/ 简介:这只是个java爬虫框架,具体使用需要个人去定制,没有图片验证,不能获取js渲染的网页,但简单易用,可以通过xpat ...
- 谈iOS抓包:Mac下好用的HTTP/HTTPS抓包工具Charles
在Mac下做开发,用Fiddler抓包由于离不开Windows比较痛苦,还好有Charles,到官网http://www.charlesproxy.com/可下载到最新版本(若不支持rMBP可拖到Re ...