|
本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接C/C++后台高级服务器课程介绍详细查看课程的服务。
通信协议设计核心
(1)解析效率。
(2)可扩展、可升级。
协议设计细节
(1)数据帧的完整性判断。
(2)序列化和反序列化。
(3)协议升级,兼容性。
(4)协议安全。
(5)数据压缩。
协议设计目标
(1)解析效率:高并发场景下,解析效率决定了使用协议的CPU成本。
(2)编码长度:决定了使用协议的网络带宽和存储成本。
(3)易于实现:满足需求的协议就是好协议,不追求大而全的。
(4)可读性:决定了使用协议的调试和维护成本。
(5)兼容性:协议可能会经常升级,使用协议的双方是否可以独立升级协 议、增减协议中的字段非常重要。
(6)跨平台语言:协议适用于任何语言来实现。比如Windows/C++,Android/ava, Web/Js,IOS/object-c。
(7)安全可靠:防止数据被破解。
协议概述
协议是一种约定,通过约定,不同的进程可以对一段数据产生相同的理解,从而可以相互协 作,存在进程间通信的程序就一定需要协议。
比如不同表的插头,还需要进行各种转换,如果我们两端进行通信没有约定好协议,那彼此是不知道对方 发送的数据是什么意义。
相关视频推荐
LinuxC++丨内存泄漏的3个解决方案与原理实现
90分钟了解 Linux内存架构
LinuxC++后台服务器开发架构师免费学习地址
【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!~点击832218493加入(需要自取)
消息的完整性判断
为了能让对端知道如何给消息帧分界,目前一般有一下做法:
(1)固定大小。不推荐。
以固定几个字节数用来分界,如每个消息100个字节(不足100就填充,超过100就分包),对端每收到100个字节,就当成1个消息来解析。
(2)以特定符号分界。
如每个消息都以特定的字符来结尾(如\r\n),当在字节流中读取到该字符时, 则表明上一个消息到此为止。HTTP就是以特定符号分界。
(3)固定消息头+消息体结构。推荐。
这种结构中一般消息头部分是一个固定字节长度的结构,并且消息头中会有 一个特定的字段指定消息体的长度。收消息时,先接收固定字节数的头部,解出这个消息完整长度, 按此长度接收消息体。这是以前各种网络应用过的最多的一种消息格式;header + body。
(4)特殊字符+消息长度+分隔符。
在序列化后的buffer前面增加一个字符流的头部,其中有个字段存储消息总长度,根据特殊字符(比 如根据\n或者\0)判断头部的完整性。这样通常比3要麻烦一些,HTTP和REDIS采用的是这种格式。 收消息的时候,先判断已收到的数据中是否包含结束符,收到结束符后解析消息头,解出这个消息完 整长度,按此长度接收消息体。
协议设计
(1)消息边界。使用什么方式界定消息边界。
(2)版本区分。版本号放在何处合适。
(3)消息类型区分。对应不同的业务。
协议设计不是为了通用,主要是为了适合业务,避免臃肿。
示例1:即时通信的协议设计]
注意:
(1)length一定要约定好是body的长度还是header+body的长度。
(2)版本号尽量靠前,是为了版本升级的便携性,反正不同版本的后续字段不同导致的未知问题。
(3)内部有不同业务,可以考虑使用appid来做识别。
(4)消息类型的识别。比如登录业务和消息聊天业务,登录有登录请求和响应等,消息聊天又有私聊和群里等。
(5)消息序列号主要用来业务的应答。判断消息是否已被接收处理成功,要不要重发等。TCP数据传输可靠不代表业务可靠。
(6)一般来说,设计协议的时候要留一些预留位,为了后期有变动或扩展时能兼容。
示例2:云平台节点服务器
注意:这里有一个STAG用于标志数据包的开始,其他和上面的含义类似。
示例3:nginx
typedef struct{
ngx_char_t magic[2]; //magic number
ngx_short_t version; // protocol version
ngx_short_t type; // protocol type: json、xml、binary、....
ngx_short_t len; // body length
ngx_uint_t seq; // message number
ngx_short_t id; // message id
ngx_char_t reserve[2]; // reserve
} ngx_message_head_t;示例4:HTTP协议
HTTP协议是最常用的协议。但是这个一般是不适合采用HTTP协议作为互联网后台的协议,主要是考虑到以下2个原因:
(1) HTTP协议只是一个框架,没有指定包体的序列化格式,所以还需要配合其他序列化的格式使用才能传 递业务逻辑数据。
(2)HTTP协议解析效率低,而且比较复杂(不知道有没有人觉得HTTP协议简单,其实不是http协议简单, 只是HTTP一家比较熟悉而已) 。
有些情况下是可以使用HTTP协议的:
(1)对公用账户api,HTTP协议的穿透性最好,所以最适合;
(2)效率要求没那么严的场景;
(3) 希望提供更多熟悉的接口,比如新浪微、腾讯博提供的开放接口。
HTTP的body是文本还是二进制?
这依赖于是否压缩,如果没有压缩就是文本;如果压缩了就是二进制,需要客户端解压成文本;如果传输的是视频流或图片,那么body就是二进制的。头部一定是文本的。
示例5:redis协议
基本原理是:先发送一个字符串表示参数个数,然后再逐个发送参数,每个参数发送的时候,先发送一个 字符串表示参数的数据长度,再发送参数的内容。
在redis 中,一些数据的类型通过它的第一个字节进行判断:
(1)单个(Simple Strings)回复:回复的第一个字节是 “+” 。
(2)错误(Errors)信息:回复的第一个字节是 “-” 。
(3)整形数字(Integers):回复的第一个字节是 “:” 。
4)多个字符串(Bulk Strings):回复的第一个字节是 “$” 。
(5)数组(Arrays):回复的第一个字节是 “*”。
此外,redis能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。 在redis中,协议的不 同部分始终以“\r\n”(CRLF)结束。
序列化方法
(1)TVL编码及其变体(TVL是tag,length和value的缩写):比如protobuf。
(2)文本流编码:比如xml、json。
(3)固定结构编码:基本原理是,协议约定了传输字段类型和字段含义,和TLV的式类似,但是没有了 tag和len,只有value,如TCP/IP。
(4)内存dump:基本原理是,把内存中的数据直接输出,不做任何序列化操作。反序列化的时候,直接还 原内存。
常见序列化方法
主流序列化协议:xml,json,protobuf。
(1)XML指可扩展标记语句(eXtensible Markup Language)。是一种通用和重量级的数据交换格式。 以文本格式存储。
(2) JSON(JavaScript ObjectNotation, JS 对象简谱) 是一种通用和轻量级的数据交换格式。以文本结构 进行存储。
(3)protocol buffer是Google的一种独立和轻量级的数据交换格式。以二进制结构进行存储。
序列化结果数据对比
XML:
<?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; ?>
<?xml-stylesheet type=&#34;text/css&#34; href=&#34;test.css&#34;?>
<test>
<name>HelloWorld</name>
<sex>male</sex>
<birthday>7.1</birthday>
<skill>AI</skill>
</test>
JSON:
{
&#34;name&#34;: &#34;HelloWorld&#34;,
&#34;age&#34;: 80,
&#34;languages&#34;: [&#34;C&#34;,&#34;linux&#34;,&#34;C++&#34;],
&#34;phone&#34;: {
&#34;number&#34;: &#34;12345678901&#34;,
&#34;type&#34;: &#34;home&#34;
},
&#34;china&#34;: true,
&#34;books&#34;:[
{
&#34;name&#34;: &#34;Linux c development&#34;,
&#34;price&#34;: 18.8
},
{
&#34;name&#34;: &#34;Linux server development&#34;,
&#34;price&#34;: 188.8
}
],
}
protobuf:
16 36 16 36 00 00 16 36 16 36 16 36 16 36 00 00 sf
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 sf
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 sf
00 00 00 00 00 00 9C 00 00 00 00 00 E7 00 36 76 sf
11 11 40序列化方法对每个字段有边界的约束。比如xml中的< name>是字段开始,< /name>是字段结束。
为什么需要序列化?因为字段值是变长的,需要一个方法约束起始和接收的边界。
序列化、反序列化速度对比
测试10W+。
序列化:
可以看到,同样是json,为什么序列化后数据大小不一样?这是由于排版的问题,比如不换行不缩进,紧凑占用的字节就少了。
反序列化:
和序列化的速度差不多的。
数据安全
数据加密。
(1)AES
(2)openssl
(3)Signal protocol端到端的通讯加密协议。
数据压缩
文本情况下压缩,二进制压缩没有太多意义。
(1)defate
(2)gzip
(3)lzw
协议升级
协议升级:增加字段。
(1)通过版本号指明协议版本,即是通过版本号辨别不同类型的协议 。
(2) 用持协议头部可扩展,即是在设计协议头部的时候有一个字段用来指明头部的长度。
总结
通信协议设计的核心目标是为了解析效率、可扩展、可升级;高并发下的通信协议应该高解析效率、易于实现、兼容性强、跨语言、安全可靠。
消息帧的完整性判断方式有:固定长度(不推荐)、header+body(推荐)、以特定符号分界、特殊字符+消息长度+分隔符。
序列化方法有:TVB编码及变体、文本流编码、固定结构编码、内存dump。
主流序列化协议有XML、JSON、protobuf。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|