从零开始学netty——自定义协议

在看此篇内容时需要浏览下面内容
从零开始学netty——如何面对粘包和拆包

自定义协议

前篇说道解决粘包和拆包的方法中有自定义协议。下文主要围绕这个问题展开。

其实自定义协议就是自己定义的一个简单规范而已。我举个常见的例子,长度+内容。就是我先说我发多长的信息,然后再给信息的内容。假如我要发送bbbb,我会先传一个数字4,表示我总共发送4个字节的数据,后面就需要再读取4个字节。

自定义协议的好处

相比其他的解决方式,自定义协议解决了定长带来的问题,现在长度也发送出去了,只需要根据接收到的长度来获取信息,不用考虑补充空位的做法了。同时也没有分隔符带来的问题,传输内容的特殊字符不会影响。

自定义协议的缺点

刚才只是举了一个简单的例子,长度+内容。如果说我有很多类型的时候,我就需要对每个类型做先长度后内容的编码。有人也会问长度又是如何保证的呢。如何正确获取一个长度呢。其实用的方法也是定长的做法,例如int,他是4个字节,那我们就读4个字节就好。其实他的开发相对其他的解决方法还是比较麻烦的。netty的ByteBuf帮我们提供了读取很多类型的方法,解决了我们部分的问题,例如读取int只需要调用readInt就好。让我们字节专注协议的设计和开发。

长度+内容 demo

client的我们发送一个字符串,先发送长度,再发送内容,这里使用了ByteBuf来处理类型的问题。

	public static ByteBuf write() {
		ByteBufAllocator bufAllocator = PooledByteBufAllocator.DEFAULT;
		ByteBuf buffer = bufAllocator.heapBuffer();
		byte[] datas = "hello".getBytes();
		buffer.writeInt(datas.length);
		buffer.writeBytes(datas);
		return buffer;

	}

服务器端,编写字节的解码器来做操作。

public class MyDecoder extends ByteToMessageDecoder {

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		int readerIndex = in.readerIndex();
		int readableBytes = in.readableBytes();
//协议自己的内容,我们自定义类长度+内容,长度还是个int,所以读取的内容起码是4个字节
		if (readableBytes > 4) {
			int dataLength = in.readInt();
			int readableData = in.readableBytes();
			if (dataLength <= readableData) {
				byte[] bytes = new byte[dataLength];
				in.readBytes(bytes);
				out.add(new String(bytes));
			} else {
              //读取内容不够,还原位置
				in.readerIndex(readerIndex);
			}

		} else {
			return;
		}


	}

}

in.readerIndex()是一个必须的操作。因为这里可能发生拆包现象,我们读取了一部分发现数据没有全部到,那就需要把位置还原到最开始的位置,下次再从那个位置开始读取。

自定义协议小结

上面只是举了一个最简单的例子,真实的协议可能比想象的复杂,例如是int + int +long+bytes。所以编写编码器和解码器能方面很多的工作。编写解码器的时候,要做对拆包情况的处理,起码保证读取到足够的长度。

猜你喜欢

转载自my.oschina.net/xpbob/blog/1810623