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客户端与 ...
随机推荐
- Linux下安装nginx
一直会使用nginx,也学习了好多nginx知识.也在本地安装过nginx,这次是第一次在正式的环境安装nginx,把这些记录下来总结经验. 一.安装环境 操作系统:CentOS release 6. ...
- 轻量级C#编辑器RoslynPad
简介 RoslynPad是一个Apache 2.0协议开源的轻量级C#编辑器.支持自动完成,语法提示,修改建议等功能.很适合平时随手写个C#程序看看运行结果. 目前版本:0.10.1,无需保存也可以运 ...
- angularJS(4)
angularJS(4) 一:angulaJs的作用域scope Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带.scope 是一个 JavaScr ...
- 虚拟机+apache+php+mysql 环境安装配置
虚拟机的安装:直接下一步即可,注意修改路径. 安装完成后新建虚拟机,直接下一步.如果选择镜像文件后出现错误,可以试着去修改电脑bios中的虚拟化设置,改为enable,如下图: apache安装: 1 ...
- PHP的GD库
GD库 PHP通过GD库,可以对JPG.PNG.GIF.SWF等图片进行处理.GD库常用在图片加水印,验证码生成等方面. 绘制线条 要对图形进行操作,首先要新建一个画布,通过imagecreatetr ...
- 洛谷P2256 一中校运会之百米跑
题目背景 在一大堆秀恩爱的**之中,来不及秀恩爱的苏大学神踏着坚定(?)的步伐走向了100米跑的起点.这时苏大学神发现,百米赛跑的参赛同学实在是太多了,连体育老师也忙不过来.这时体育老师发现了身为体育 ...
- 在windows上部署使用Redis
参考地址: http://keenwon.com/1275.html
- css学习笔记(2)
----------------|-------------------------|------------------ 标签名 英文全拼 中文 ...
- UBER的故事
今天分享一个很好的视频,19分钟,这个视频讲了过去几年一家伟大公司的成长的思路,这个演讲的PPT很棒,演讲者的语速.语调.表情等也非常适合大家在日常工作中学习. 链接:http://v.youku ...
- Apache报错信息之Invalid command 'Order', perhaps misspelled or defined by a module not included in the server config
今天配置开启Apache虚拟主机时, 然后日志报错提示: Invalid command 'Order', perhaps misspelled or defined by a module not ...