netty 提供的解碼器
DelimiterBasedFrameDecoder 解決TCP的粘包解碼器
StringDecoder 消息轉成String解碼器
LineBasedFrameDecoder 自動完成標識符分隔解碼器
FixedLengthFrameDecoder 固定長度解碼器,二進制
Base64Decoder base64 解碼器
假設一包數(shù)據(jù)定義如下
netty解碼部分核心代碼如下
public class ObdProtocol extends ChannelInitializer<SocketChannel> {
private RabbitSender rabbitSender;
private int port;
public ObdProtocol(RabbitSender rs, int tcpPort) {
this.rabbitSender = rs;
this.port = tcpPort;
}
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new IdleStateHandler(6 * 60, 0, 0));
pipeline.addLast(new HeartBeatHandler());
//分割符
ByteBuf delimiter = Unpooled.copiedBuffer(new byte[]{-25});
//打印分隔符
pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(1024,false, delimiter));
pipeline.addLast("objectEncoder", new ObdProtocolEncoder());
pipeline.addLast("objectDecoder", new ObdServiceHandler(rabbitSender, port));
}
}
接收handler 部分代碼
@Slf4j
public class ObdServiceHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
//轉換成大寫16進制字符串
String hexData = ByteBufUtil.hexDump(byteBuf).toUpperCase();
//打印接收數(shù)據(jù)
System.out.println(hexData);
}
}
測試
測試工具:NetAssist發(fā)了兩包數(shù)據(jù) netty 已經(jīng)在底層給我們處理好了粘包的問題
再測試當數(shù)據(jù)為半包的情況下
先發(fā)送數(shù)據(jù)半包數(shù)據(jù) E712 在發(fā)送一包半包數(shù)據(jù) 11E7E7D022E7 結果會打印兩包數(shù)據(jù)。很是ok 。 但是真實的需求往往不是這樣子的。比如 原始數(shù)據(jù)是 E71211E7E7D022E7淋纲。往往需要解出來的是帶開始和結束標識的兩包數(shù)據(jù)。如下
第一包數(shù)據(jù): E71211E7 第二包數(shù)據(jù) : E7D022E7。 你可能說很簡單兄猩,把 DelimiterBasedFrameDecoder 的構造函數(shù)里面的stripDelimiter 設置為false 不就行了嗎?
但是看結果:
和想要的結果差點鉴未。應為netty 根據(jù)E7 為分隔符枢冤,把數(shù)據(jù)拆分成了四包數(shù)據(jù)。
附上netty 底層處理過程
把DelimiterBasedFrameDecoder 拷貝下來铜秆。在idexOf 加上圖審所示代碼(第一個if 是解決截取消息同的問題淹真,第二個if 是為了解決粘包問題)
詳細的操作步驟有空再單獨寫一篇文章吧!起始跟著源碼調試一下就明白了 很簡單的连茧。
修改之后測試
半包測試
提示成功發(fā)送但是在handler里面并沒有收到任何消息
下面 再發(fā)剩下的一包多的數(shù) E7E7D022E7
依然是兩包完整的數(shù)據(jù)。