Netty自定义TCP拆包解码器
定义开始、结束标识字节数组
这里使用###START###
作为开始标识,###STOP###
作为结束标识,代码如下:
public class CodecConstants {
public static final byte[] BEGIN_DELIMITER = new byte[]{ 0x23, 0x23, 0x53, 0x54, 0x41, 0x52, 0x54, 0x23, 0x23 }; // 协议开始标识
public static final byte[] END_DELIMITER = new byte[]{ 0x23, 0x23, 0x45, 0x4e, 0x44, 0x23, 0x23 }; // 协议结束标识
}
也可以直接通过"自定义标识".getBytes()
拿到字节数组。
自定义拆包解码器类
public class CustomBasedFrameDecoder extends ByteToMessageDecoder {
private static final ByteBuf beginDelimiter = Unpooled.wrappedBuffer(CodecConstants.BEGIN_DELIMITER);
private static final ByteBuf endDelimiter = Unpooled.wrappedBuffer(CodecConstants.END_DELIMITER);
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int beginDelimiterLength = beginDelimiter.readableBytes(); // 开始标识字节长度
int endDelimiterLength = endDelimiter.readableBytes(); // 结束标识字节长度
int beginIdx = indexOf(in, beginDelimiter); // 开始标识下标
int endIdx = indexOf(in, endDelimiter); // 结束标识下标
// 如果缓存区找不到开始标识,跳过该段字节
if (beginIdx == -1) {
in.readerIndex(in.writerIndex());
return;
}
// 如果缓冲区找不到结束下标,或者下标小于开始标识,则从开始标识开始读取
if (endIdx == -1 || endIdx < beginIdx) {
in.readerIndex(beginIdx);
return;
}
ByteBuf frame = in.retainedSlice(beginIdx + beginDelimiterLength, endIdx - beginIdx - beginDelimiterLength); // 将标识之间的数据提取出来,给后面的解码器使用
out.add(frame);
in.readerIndex(endIdx + endDelimiterLength); // 更新读取下标
}
private static int indexOf(ByteBuf haystack, ByteBuf needle) {
for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i++) {
int haystackIndex = i;
int needleIndex;
for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex++) {
if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) break;
haystackIndex++;
if (haystackIndex == haystack.writerIndex() && needleIndex != needle.capacity() - 1) return -1;
}
if (needleIndex == needle.capacity()) return i - haystack.readerIndex();
}
return -1;
}
}
拆包解码器到这里就结束了,使用方式如下:
bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class)
.group(work)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) {
channel.pipeline()
.addLast(new XXXEncoder())
.addLast(new CustomBasedFrameDecoder()) // 注意先后顺序,要记得在其他解码器前面添加
.addLast(new XXXDecoder()) // 对分拆出来的字节进行处理
.addLast(new XXXXHandler()); // 最后添加数据处理器
}
});