While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either that the input has been truncated or that an embedded message misreported its own length.
出现上述这个问题我觉得是您使用protlcol的方式不对;也就是出现了粘包拆包
的问题; 上一篇文章介绍了Netty中使用Protobuf的编码解码,下面介绍一下粘包拆包的问题;
什么是粘包、拆包?
在基于流的传输里比如TCP/IP,接收到的数据会先被存储到一个socket接收缓冲里。不幸的是,基于流的传输并不是一个数据包队列,而是一个字节队列。即使你发送了2个独立的数据包,操作系统也不会作为2个消息处理而仅仅是作为一连串的字节而言。 因此这是不能保证你远程写入的数据就会准确地读取。举个例子,让我们假设操作系统的TCP/TP协议栈已经接收了3个数据包:
由于基于流传输的协议的这种普通的性质,在你的应用程序里读取数据的时候会有很高的可能性被分成下面的片段
因此,一个接收方不管他是客户端还是服务端,都应该把接收到的数据整理成一个或者多个更有意思并且能够让程序的业务逻辑更好理解的数据。在上面的例子中,接收到的数据应该被构造成下面的格式:
解决方法由如下几种:
1、消息定长,报文大小固定长度,例如每个报文的长度固定为200字节,如果不够空位补空格;
2、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分;
3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段;
Netty提供了多个解码器,可以进行分包的操作,分别是:
LineBasedFrameDecoder (利用换行符解决)
DelimiterBasedFrameDecoder(添加特殊分隔符报文来分包)
FixedLengthFrameDecoder(使用定长的报文来分包)
LengthFieldBasedFrameDecoder
复制代码
这里我们介绍一下LineBasedFrameDecoder
(利用换行符解决)的使用方式:
LineBasedFrameDecoder 解码器使用非常简单,只需要在 pipline 链条上添加即可。
//字符串解析,换行防拆包
.addLast(new LineBasedFrameDecoder(1024))
.addLast(new StringDecoder())
复制代码
构造函数中传入了 1024 是指报的长度最大不超过这个值;
注意,由于 LineBasedFrameDecoder 解码器是通过换行符来判断的,所以在发送时,一条完整的消息需要加上 \n。
例如:
Protocol 拆、粘包接下来我们使用Protocol 来解决,拆、粘包,用Protocol来解决拆、粘包那是相当简单的. 只需要在服务端和客户端加上这两个编解码工具即可:
//拆包解码
.addLast(new ProtobufVarint32FrameDecoder())
.addLast(new ProtobufVarint32LengthFieldPrepender())
复制代码
但是注意一点,添加pipeline链的时候一定要注意顺序(我在写案例的时候遇到的);具体请参考本案例代码;
这个编解码工具可以简单理解为是在消息体中加了一个 32 位长度的整形字段,用于表明当前消息长度。
demo 地址:
当前项目:Netty框架--netty编码解码(项目中proto包下)
多线程并发编程(项目中thread包下)
BIO编程(项目中bio包下)
NIO编程(项目中nio包下)
自定义RPC(项目中rpc包下)
欢迎给个小星星鼓励一下(记得Star哦,害羞脸).... 我只是给代码注入灵魂