继大纲要点(点击查看),先从编解码入手,因为编解码弄清楚了,才好弄清楚请求,而协议是客户端和服务端的连接器。
该篇文件,以tars_encode示例逐步深入。
1、打开tars_encode项目
进入tars_encode项目目录
cd /home/lirizhong/tars/src/tarscpp/tarscpp/example/tars_encode
使用vscode打开tars_encode项目
code .
目录结构如下:
UserInfo.tars文件内容如下:
module example
{
struct FriendInfo
{
1 require int uin;
2 require string nick;
3 optional vector<byte> birthday;
};
struct UserInfo
{
1 require int qq;
2 require string nick;
3 require vector<byte> birthday;
5 require map<short, FriendInfo> friends;
4 optional string city = "gd";
};
interface User
{
int test(UserInfo stReq, UserInfo stRsp);
};
};
UserInfo.h是根据UserInfo.tars生成的代码文件,工程在构建时会自动生成。如果手痒痒,也可以手动执行如下命令:
cd src
/usr/local/tars/cpp/compiler/tars2cpp UserInfo.tars
2、编解码基本数据类型
typedef bool Bool; /
typedef char Char;
typedef short Short;
typedef float Float;
typedef double Double;
typedef int Int32;
typedef unsigned char UInt8;
typedef unsigned short UInt16;
typedef unsigned int UInt32;
#if __WORDSIZE == 64
typedef long Int64;
#else
typedef long long Int64;
#endif
支持的基本数据类型包括布尔类型、字节型、短整数、浮点数、双精度浮点数、整数、长整数、字符串、列表和字典。
Tars编解码的编码器由TarsOutputStream<tars::BufferWriterVector>实现流式操作,
而解码器由TarsInputStream<BufferReader>实现流式操作,看下具体代码:
基本数据类型编码:
vector<char> encodeBasic()
{
// 基础数据类型编码
uint8_t tag = 1;
tars::TarsOutputStream<tars::BufferWriterVector> os;
// 姓名
string name = "tars";
os.write(name, tag++);
// 年龄
uint8_t age = 10;
os.write(age, tag++);
// 出生时间
int64_t birth = 1627999336;
os.write(birth, tag++);
// 身高
float height = 1.12345678901234567890;
os.write(height, tag++);
// 体重
double weight = 65.12345678901234567890;
os.write(weight, tag++);
// 爱好程度
map<string, int> fav;
fav["basketball"] = 80;
fav["football"] = 60;
fav["sleep"] = 90;
os.write(fav, tag++);
// 技能
vector<string> skill;
skill.push_back("programming");
skill.push_back("selling");
os.write(skill, tag++);
return os.getByteBuffer();
}
基本数据类型解码:
void decodeBasic(const vector<char> &vRsp)
{
try
{
uint8_t tag = 1;
TarsInputStream<BufferReader> is;
is.setBuffer(vRsp);
// 姓名
string name = "";
is.read(name, tag++);
// 年龄
uint8_t age = 0;
is.read(age, tag++);
// 出生时间
int64_t birth = 0;
is.read(birth, tag++);
// 身高
float height = 0.00f;
is.read(height, tag++);
// 体重
double weight = 0.00;
is.read(weight, tag++);
// 爱好程度
map<string, int> fav;
is.read(fav, tag++);
// 技能
vector<string> skill;
is.read(skill, tag++);
cout << "---------------------------------------------------------------" << endl;
cout << "name:" << name << ",age:" << int(age) << ",birth:" << birth << ",height:" << height << ",weight:" << weight << endl;
map<string, int>::const_iterator itFav = fav.begin();
for (; itFav != fav.end(); itFav++)
{
cout << itFav->first << ":" << itFav->second << endl;
}
for (size_t i = 0; i < skill.size(); i++)
{
cout << skill.at(i) << endl;
}
}
catch (const std::exception &e)
{
std::cerr << "decodeBasic exception:" << e.what() << endl;
}
}
3、编解码高级数据类型
支持的高级数据类型主要为结构体,即向应用层提供了抽象数据类型的能力。结合基本数据类型,如整数、列表、字典等可实现复杂的结构体。
需要支持Tars编解码的结构体,一般需要继承tars::TarsStructBase这个基础结构体。如UserInfo.h中的UserInfo和FriendInfo结构体均继承tars::TarsStructBase。另外必须实现如下编解码方法:
template<typename WriterT>
void writeTo(tars::TarsOutputStream<WriterT>& _os) const
{
_os.write(uin, 1);
_os.write(nick, 2);
if (birthday.size() > 0)
{
_os.write(birthday, 3);
}
}
template<typename ReaderT>
void readFrom(tars::TarsInputStream<ReaderT>& _is)
{
resetDefautlt();
_is.read(uin, 1, true);
_is.read(nick, 2, true);
_is.read(birthday, 3, false);
}
看下具体代码:
高级数据类型编码:
vector<char> encodeStruct()
{
// 高级数据类型编码
uint8_t tag = 1;
tars::TarsOutputStream<tars::BufferWriterVector> os;
string key = "三国演义";
os.write(key, tag++);
UserInfo user;
user.qq = 223;
user.nick = "刘备";
user.city = "深圳";
user.birthday.push_back('2');
user.birthday.push_back('2');
user.birthday.push_back('3');
FriendInfo friendZF;
friendZF.uin = 221;
friendZF.nick = "张飞";
friendZF.birthday.push_back('2');
friendZF.birthday.push_back('2');
friendZF.birthday.push_back('1');
user.friends[221] = friendZF;
FriendInfo friendGY;
friendGY.uin = 220;
friendGY.nick = "关羽";
friendGY.birthday.push_back('2');
friendGY.birthday.push_back('2');
friendGY.birthday.push_back('0');
user.friends[220] = friendGY;
os.write(user, tag++);
return os.getByteBuffer();
}
高级数据类型解码:
void decodeStruct(const vector<char> &vRsp)
{
try
{
uint8_t tag = 1;
TarsInputStream<BufferReader> is;
is.setBuffer(vRsp);
string key = "";
is.read(key, tag++);
UserInfo user;
is.read(user, tag++);
cout << "---------------------------------------------------------------" << endl;
cout << "key:" << key << endl;
user.display(cout);
}
catch (const std::exception &e)
{
std::cerr << "decodeStruct exception:" << e.what() << endl;
}
}
看完上面,可能不知其所以然,会用应该没问题,依样划葫芦吧。
如果想深究Tars的底层编解码规则,查看TarsOutputStream和TarsInputStream的具体实现吧,少年。不过在这里还是简单提一句:TLV和TV。
TLV:Tag|Type + Length + Value
TV:Tag|Type + Value
无论是TLV还是TV,每个字段都有一个唯一标识Tag,且都带上数据类型信息Type,而如果数据类型是定长数据类型时,数据类型信息Type就已经表明了其长度,因此无须将长度再编码进去,否则画蛇添足,多此一举了。
另外,据Tars官方表示,Tars编解码性能比ProtoBuffer性能还要更优。我没有测试过,不过看代码实现,确实挺高效的,我相信腾讯,谁叫我用了腾讯的Tars微服务框架呢。
本文暂时没有评论,来添加一个吧(●'◡'●)