tunm二进制协议在python上的实现

tunm是一种对标JSON的二进制协议, 支持JSON的所有类型的动态组合

支持的数据类型

基本支持的类型 “u8”, “i8”, “u16”, “i16”, “u32”, “i32”, “u64”, “i64”, “varint”, “float”, “string”, “raw”, “array”, “map”

为什么我们需要二进制协议

下图是文本格式JSON与tunm的对比

类型 可读 可编辑 编码速度 解码速度 数据大小 预定义 JSON ✓ ✓ 慢 慢 大 否 tunm x x 快 快 小 否 protobuf x x 快 快 小 是

在高性能的场景下, 或者需要流量传输比较敏感的地方, 通常会选择二进制来代替文本协议来做为通讯的, 如RPC, REST, 游戏等情况。
相对于google protobuf, 它需要比较完善的预定义过程, 就比如客户端版本1, 服务端版本2, 就有比较大的可能造成不兼容, 对需求经常变化的就会比较难与同步。
tunm相对于JSON, 若第一版是

{
    "name": "tunm", "version": 1
}

此时第二版需要加入用户的id, 就可以很方便的变成

{
    "name": "tunm", "version": 2, "id": 1
}

而对客户端1来说, 只是多一个id的字段, 不会有任何的破坏, 做到版本升级而无影响

协议的二进制格式

数据协议分为三部分(协议名称, 字符串索引区, 数据区(默认为数组))
如数据协议名为cmd_test_op, 数据为[“tunm_proto”, {“name”: “tunm_proto”, “tunm_proto”: 1}]

协议的实现(小端对齐)

ByteBuffer具有组装字节流的功能, 比如写入字符串, 写入int, 还有里面存储字符串索引区

class ByteBuffer(object):
    def __init__(self):
        # 字节缓冲区
        self.buffer = bytearray([00]*1024)
        # 写入的位置索引号
        self.wpos = 0
        # 读出的位置索引号
        self.rpos = 0
        # 大小端格式
        self.endianness = "little"
        # 索引的数组及快速查询的字符串索引号
        self.str_arr = []
        self.str_map = {}
@enum.unique
class TP_DATA_TYPE(IntEnum):
    TYPE_NIL = 0,
    TYPE_BOOL = 1,
    TYPE_U8 = 2,
    TYPE_I8 = 3,
    TYPE_U16 = 4,
    TYPE_I16 = 5,
    TYPE_U32 = 6,
    TYPE_I32 = 7,
    TYPE_U64 = 8,
    TYPE_I64 = 9,
    TYPE_VARINT = 10,
    TYPE_FLOAT = 11,
    TYPE_DOUBLE = 12,
    TYPE_STR = 13,
    TYPE_STR_IDX = 14,
    TYPE_RAW = 15,
    TYPE_ARR = 16,
    TYPE_MAP = 17,
@staticmethod
def encode_varint(buffer: ByteBuffer, value):
    '''
    如果原数值是正数则将原数值变成value*2
    如果原数值是负数则将原数值变成-(value + 1) * 2 + 1
    相当于0->0, -1->1, 1->2,-2->3,2->4来做处理
    因为小数值是常用的, 所以保证小数值及负数的小数值尽可能的占少位
    '''
    if type(value) == bool:
        value = 1 if value else 0
    real = value * 2
    if value < 0:
        real = -(value + 1) * 2 + 1

    for _i in range(12):
        # 每个字节的最高位来表示有没有下一位, 若最高位为0, 则已完毕
        b = real & 0x7F
        real >>= 7
        if real > 0:
            buffer.write_u8(b | 0x80)
        else:
            buffer.write_u8(b)
            break
@staticmethod
def encode_str_idx(buffer: ByteBuffer, value):
    '''
    写入字符串索引值, 在数值区里的所有字符串默认会被写成索引值
    如果重复的字符串则会返回相同的索引值(varint)
    '''
    idx = buffer.add_str(value)
    TPPacker.encode_type(buffer, TP_DATA_TYPE.TYPE_STR_IDX)
    TPPacker.encode_varint(buffer, idx)
@staticmethod
def encode_field(buffer: ByteBuffer, value, pattern=None):
    '''
    先写入类型的值(u8), 则根据类型写入类型对应的的数据
    '''
    if not pattern:
        pattern = TPPacker.get_type_by_ref(value)
    if pattern == TP_DATA_TYPE.TYPE_NIL:
        return None
    elif pattern == TP_DATA_TYPE.TYPE_BOOL:
        TPPacker.encode_type(buffer, pattern)
        TPPacker.encode_bool(buffer, value)
    elif pattern >= TP_DATA_TYPE.TYPE_U8 and pattern = TP_DATA_TYPE.TYPE_U16 and pattern
@staticmethod
def encode_proto(buffer: ByteBuffer, name, infos):
    '''
    写入协议名称, 然后写入字符串索引区(即字符串数组), 然后再写入协议的详细数据
    '''
    sub_buffer = ByteBuffer()
    TPPacker.encode_field(sub_buffer, infos)

    TPPacker.encode_str_raw(buffer, name, TP_DATA_TYPE.TYPE_STR)
    TPPacker.encode_varint(buffer, len(sub_buffer.str_arr))
    for val in sub_buffer.str_arr:
        TPPacker.encode_str_raw(buffer, val, TP_DATA_TYPE.TYPE_STR)

    buffer.write_bytes(sub_buffer.all_bytes())

Original: https://www.cnblogs.com/luojiawaf/p/16411608.html
Author: 行稳致远up
Title: tunm二进制协议在python上的实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/499217/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球