这是我参与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)自定义协议
目前市面上已经有不少通用的协议,例如 HTTP、HTTPS、JSON-RPC、FTP、IMAP、Protobuf 等。
自定义协议主要有以下优点:
- 性能:相比通用协议
- 扩展性:自定义的协议相比通用协议更好扩展,可以更好地满足自己的业务需求。
- 安全性:通用协议是公开的,很多漏洞已经很多被黑客攻破。自定义协议更加安全,因为黑客需要先破解你的协议内容。
Netty 作为一个非常优秀的网络通信框架,已经为提供了非常丰富的编解码抽象基类,更方便地基于这些抽象基类扩展实现自定义协议。
Netty 常用编码器类型:
MessageToByteEncoder对象编码成字节流;MessageToMessageEncoder一种消息类型编码成另外一种消息类型。
Netty 常用解码器类型:
ByteToMessageDecoder/ReplayingDecoder将字节流解码为消息对象;MessageToMessageDecoder将一种消息类型解码为另外一种消息类型。
编解码器可以分为 一次解码器 和 二次解码器。
一次 解码器用于解决 TCP 拆包/粘包问题,按协议解析后得到的字节数据。
如果你需要对解析后的字节数据做对象模型的转换,这时候便需要用到二次解码器,同理编码器的过程是反过来的。
- 一次编解码器:
MessageToByteEncoder/ByteToMessageDecoder。 - 二次编解码器:
MessageToMessageEncoder/MessageToMessageDecoder。
如何判断 ByteBuf 是否存在完整的报文?
最常用的做法就是通过读取消息长度
dataLength进行判断。如果ByteBuf的可读数据长度小于dataLength,说明ByteBuf还不够获取一个完整的报文。
(2)Protobuf 使用
官网下载 Protobuf: 官网
- 下载:
protobuf-java-3.9.1.tar.gz - 安装 (环境
Ubuntu 18)
cd protobuf-3.9.1
./autogen.sh
./configure
make
make check
sudo make install
protoc --version
- 编译生成文件:对应目录下会生成对应的文件
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; // 数据长度
}