如何自定义协议

前言

何为自定义协议,实际上是相对标准协议来讲的,这里主要针对的是应用层协议;常见的标准的应用层协议如http、ftp、smtp等,若是咱们在网络通讯的过程当中不去使用这些标准协议,那就须要自定义协议,好比咱们经常使用的RPC框架(dubbo,thrift),分布式缓存(redis,memcached)等都是自定义协议;本文就来说讲如何去自定义私有协议,在此以前咱们先考虑一下为何要自定义协议。html

为何要自定义协议

直接使用标准的协议好处是显而易见的,我我的理解的几点优势:面试

  • 既然是标准协议说明已经成为了标准,这样不少系统就能够直接对接,无缝集成;
  • 协议最重要的一点就是编码解码,标准协议每每有现成的编码解码包,直接拿来使用,减小开发时间;
  • 有不少围绕标准协议的第三方测试工具,能够很方便的进行测试;

既然有这么多优势那咱们为何还要去自定义协议,大体出于如下几点考虑:redis

  • 既然是标准协议,每每兼顾的东西比较多,致使协议数据相对来讲比较大,这样可能在一些追求性能,流量的系统中不能容忍;
  • 标准协议有不少,没有哪种协议能够适用任何场景中,因此若是在某个场景中尚未既定的标准协议,这时候会有各类私有协议;
  • 自定义协议只要双方约定好数据结构就行,不具备通用性,理论上来讲会更加安全一点,固然如今不少标准协议都有安全版本,好比https,sftp等等;

以上只是我的的一点理解,欢迎你们补充;关于如何去自定义协议,其实能够去多参考一些主流的标准协议或者私有协议,其实有不少共同点能够去借鉴;下面先简单看看那些主流的协议;算法

主流协议

下面分别看看一些主流的标准协议或者私有协议都是如何去定义本身的数据结构的,对咱们有很是好的借鉴意义;数据库

http协议

http协议你们最熟悉不过了,全称叫超文本传输协议,整个请求报文能够分为三个部分分别是:请求行,请求报头,请求正文;json

  • 请求行
GET /test.html HTTP/1.1 (CRLF换行)
  • 请求报头
Accept-Encoding: gzip, deflate
Content-Length: 38 
Content-Encoding: gzip
...

请求包头有不少,每个表明了各自的含义,这边就不一一列出,咱们这里更加关注整个报文的结构;segmentfault

  • 请求正文

这个只有在POST请求的时候才有正文,里面存放业务数据,好比常见的json文本串;具体正文的长度能够根据消息头中的Content-Length来决定;缓存

dubbo协议

dubbo协议格式能够直接参考官网提供的以下图片:
image
看上图其实整个协议数据包也大体分为两个部分:固定部分和可变部分,或者叫消息头和消息体;
固定部分一共是4+8+4=16个字节,具体以下所示:安全

header{
    Magic High       = 8bit;  //魔数高位
    Magic Low        = 8bit;  //魔数低位
    Req/Res          = 1bit;  //标识是请求或响应
    2 Way            = 1bit;  //标记是否指望从服务器返回值
    Event            = 1bit;  //标识是不是事件消息
    Serialization ID = 5bit;  //标识序列化类型
    Status           = 8bit;  //标识响应的状态
    Request ID       = 64bit; //标识惟一请求
    Data Length      = 32bit; //序列化后的内容长度
}

可变部分根据固定部分中的Data Length来肯定长度;服务器

redis协议

Redis的客户端与服务端采用叫作 RESP(Redis Serialization Protocol)的网络通讯协议交换数据,相对来讲仍是比较简单的,如下是这个协议的通常形式:

*< 参数数量 > CR LF
$< 参数 1 的字节数量 > CR LF
< 参数 1 的数据 > CR LF
...
$< 参数 N 的字节数量 > CR LF
< 参数 N 的数据 > CR LF

以上大体介绍了三种比较有表明性的协议,虽说每种协议都有各自的使用场景,可是若是咱们本身去定义协议,仍是有一些相通的东西;

如何自定义协议

下面咱们重点看看去自定义协议有哪些须要咱们关注的点,如下是本人根据本身的理解整理了以下关注点:

  • 完整的数据包
  • 协议号
  • 消息头标识
  • 业务数据
  • 预留字段

下面分别逐一详细介绍:

完整的数据包

咱们平时常常讲数据包,可是TCP其实只有流的概念,并无数据包的概念;那很重要的一点就是咱们的程序怎么知道如今的业务数据已经接受所有接收完了,能够做为一个完整的数据包去处理了,若是不去作处理的话就会出现咱们常说的半包和粘包问题;主流的的处理方式大体有这么两种:

  • 在消息头部加上数据包长度描述,好比在http协议和dubbo协议中出现的dataLength字段;
  • 用特殊的字符串做为数据包的结尾,这样咱们在接受数据的时候接受到预约的特殊字符串就表示数据包完整了;

协议号

可能不一样的协议有不一样的叫法,我这里把它叫作协议号,我的理解就是根据这个协议号,服务器端知道去执行什么逻辑;好比http协议请求行中的/test.html,dubbo协议中的服务名+版本号,redis中的具体要执行什么key;

消息头标识

这个是否须要仍是要看各自的场景,好比redis协议足够简单,无需任何标识,全部的东西都是双端约定好的;可是其余不少协议仍是有一些须要的,除了上面说到的能够在消息头中指定dataLength,其实还有不少其余的东西能够指定好比:

  • 业务数据格式:文本格式,json格式,html格式等等;
  • 压缩格式:可能为了追求流量包大小对数据包进行压缩,gzip、deflater、snappy等;
  • 加密算法:可能须要对个人业务数据进行加密处理,保证业务数据的安全性AES、DES等;

业务数据

业务数据每每在整个数据包中是最大的,同时也是大小可变的部分;咱们上面所作的这些其实都是在为业务数据服务,业务数据须要在网络传输,最重要的一点就是序列化,通常就如下两种方式:

  • 文本方式:序列化文本文档text,或者json串,xml格式等;
  • 二进制方式:常见的好比protobuf,thrift,kyro等;

预留字段

是否须要预留字段这个得看状况,好比http协议整个消息头是可变的,每一行一个标识,知道读取到空行,表示消息头结束下面就是正文了,能够理解为http使用了两种方式来保证完整包,消息头使用特殊字符结尾,正文使用在消息头中指定dataLength;这种方式其实它的整个扩展性是很是好的;
另一种像dubbo这样,其实它的头部至关于已经固定好了16个字节,这种状况下是否能够预留几个字节防止后面的变动;

总结

自定义协议其实在咱们真正的工做中仍是不多能接触到的,更多的其实仍是去实现业务,可是咱们系统无时无刻不在和各类应用层协议打交道,若是咱们了解了各类协议,在系统出现问题时能够作抓包分析;另外像咱们经常使用的数据库中间件、缓存中间件等,都须要对协议都充分的了解,而后去实现代理。

感谢关注

能够关注微信公众号「 回滚吧代码」,第一时间阅读,文章持续更新;专一Java源码、架构、算法和面试。
相关文章
相关标签/搜索