编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

5、Tars C++系列之Tars编解码(srt编解码器)

wxchong 2024-07-08 01:18:00 开源技术 56 ℃ 0 评论

继大纲要点(点击查看),先从编解码入手,因为编解码弄清楚了,才好弄清楚请求,而协议是客户端和服务端的连接器。

该篇文件,以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微服务框架呢。


Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表