【IM】协议

303 阅读3分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

一、前言

客户端与服务端交互数据,需要定义协议来解析。

举个栗子,通常前后端通过HTTP交互,协议是 JSON

  • 可以看到 HTTP header 会定应 Conten他-Type:application/json
$ curl -H "Content-Type:application/json" -X POST --data '{}' http://127.0.0.1:8080/file/post -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0)
> POST http://127.0.0.1:8080/file/post HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Proxy-Connection: Keep-Alive
> Content-Type:application/json
> Content-Length: 2
> 
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 200 
< Content-Length: 4
< Connection: keep-alive
< Content-Type: text/plain;charset=UTF-8
< Date: Fri, 13 Aug 2021 08:23:11 GMT
< Keep-Alive: timeout=4
< Proxy-Connection: keep-alive
< 
* Connection #0 to host 127.0.0.1 left intact
post

IM 中一般采用:自定义协议 + protobuf

  • 自定义协议:防止外部破解

  • protobuf:数据更高高效,数据体更小

使用 protobuf 进行序列化,即对请求体是复杂的 Java 对象,序列化成二进制字节数组。


(1)自定义协议

目前市面上已经有不少通用的协议,例如 HTTPHTTPSJSON-RPCFTPIMAPProtobuf 等。

自定义协议主要有以下优点:

  • 性能:相比通用协议
  • 扩展性:自定义的协议相比通用协议更好扩展,可以更好地满足自己的业务需求。
  • 安全性:通用协议是公开的,很多漏洞已经很多被黑客攻破。自定义协议更加安全,因为黑客需要先破解你的协议内容。

Netty 作为一个非常优秀的网络通信框架,已经为提供了非常丰富的编解码抽象基类,更方便地基于这些抽象基类扩展实现自定义协议。

Netty 常用编码器类型:

  • MessageToByteEncoder 对象编码成字节流;
  • MessageToMessageEncoder 一种消息类型编码成另外一种消息类型。

Netty 常用解码器类型:

  • ByteToMessageDecoder/ReplayingDecoder 将字节流解码为消息对象;
  • MessageToMessageDecoder 将一种消息类型解码为另外一种消息类型。

编解码器可以分为 一次解码器二次解码器

一次 解码器用于解决 TCP 拆包/粘包问题,按协议解析后得到的字节数据。

如果你需要对解析后的字节数据做对象模型的转换,这时候便需要用到二次解码器,同理编码器的过程是反过来的。

  • 一次编解码器:MessageToByteEncoder/ByteToMessageDecoder
  • 二次编解码器:MessageToMessageEncoder/MessageToMessageDecoder

如何判断 ByteBuf 是否存在完整的报文?

最常用的做法就是通过读取消息长度 dataLength 进行判断。如果 ByteBuf 的可读数据长度小于 dataLength,说明 ByteBuf 还不够获取一个完整的报文。


(2)Protobuf 使用

官网下载 Protobuf官网

  1. 下载:protobuf-java-3.9.1.tar.gz
  2. 安装 (环境 Ubuntu 18
cd protobuf-3.9.1
./autogen.sh
./configure
make
make check
sudo make install
protoc --version
  1. 编译生成文件:对应目录下会生成对应的文件
protoc –java_out=im/src im/proto/AuthenticateRequest.proto

问题,若出现:

protoc: error while loading shared libraries: libprotoc.so.8: cannot open shared object file: No such file or directory

解决:

# 方法 1. 
sudo ldconfig


# 方法 2. 貌似全局无效,即使放在 /etc/profile
export LD_LIBRARY_PATH=/usr/local/lib

二、案例:RPC 协议

协议结构,如下:

@Data
public class MiniRpcProtocol<T> implements Serializable {
    private MsgHeader header;
    private T body;
}

具体 header 如下:

@Data
public class MsgHeader implements Serializable {
    /*
    +---------------------------------------------------------------+
    | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte  |
    +---------------------------------------------------------------+
    | 状态 1byte |        消息 ID 8byte     |      数据长度 4byte     |
    +---------------------------------------------------------------+
    */
    private short magic;        // 魔数
    private byte version;       // 协议版本号
    private byte serialization; // 序列化算法
    private byte msgType;       // 报文类型
    private byte status;        // 状态
    private long requestId;     // 消息 ID
    private int msgLen;         // 数据长度
}