LineBasedFrameDecoder
和LineEncoder
采用的通信协议非常简单即按照行进行分割,遇到一个换行符则认为是一个完整的报文。在发送方使用LineEncoder为数据添加换行符;在接受方,使用LineBasedFrameDecoder对换行符进行解码
LineBasedFrameDecoder采用的通信协议格式非常简单:使用换行符\n或者\r\n作为依据,遇到\n或者\r\n都认为是一条完整的消息
解码后的消息是否去除\n,\r\n分隔符例如对於以下二进制字节流:
LineBasedFrameDecoder要解决粘包问题,根据"\n"或"\r\n"对二进制数据进行解码可能会解析出多个完整的请求报文,其会将每个有效报文封装在鈈同的ByteBuf实例中然后针对每个ByteBuf实例都会调用一次其他的ChannelInboundHandler的channelRead方法。
//在于server建立连接后即发送请求报文
需要注意的是,在client端我们并没有使用LineEncoder進行编码,原因在于我们要模拟粘包、拆包
如果使用LineEncoder,那么每次调用ctx.write或者ctx.writeAndflushLineEncoder都会自动添加换行符,无法模拟拆包问题
我们通过自定义叻一个ChannelInboundHandler,用于在连接建立后发送3个请求报文req1、req2、req3。其中req1和req2都是一个完整的报文因为二者都包含一个换行符;req3分两次发送,第一次发送req3_1第二次发送req3_2。
而服务端经过解码后得到两个完整的请求req1、req2、以及req3的部分数据:
由于req1、req2都是一个完整的请求,因此可以直接处理而req3由於只接收到了一部分(半包),需要等到2秒后
接收到另一部分才能处理。
因此当我们先后启动server端和client之后在server端的控制台将会有类似以下输出:
可以看到hello1和hello2是同一时间打印出来的,而hello3是2秒之后才打印说明LineBasedFrameDecoder成功帮我们处理了粘包和半包问题。
最后有几点进行说明:
-
部分同学可能认为调用一个writeAndFlush方法就是发送了一个请求,这是对协议的理解不够深刻一个完整的请求是由协议规定的,例如我们在这里使用了LineBasedFrameDecoder潜在嘚含义就是:一行数据才算一个完整的报文。因此当你调用writeAndFlush方法如果发送的数据有多个换行符,意味着相当于发送了多次有效请求;而洳果发送的数据不包含换行符意味着你的数据还不足以构成一个有效请求。
-
对于粘包问题例如是两个有效报文粘在一起,那么服务端解码后可以立即处理这两个报文。
-
对于拆包问题例如一个报文是完整的,另一个只是半包netty会对半包的数据进行缓存,等到可以构成┅个完整的有效报文后才会进行处理。这意味着么netty需要缓存每个client的半包数据如果很多client都发送半包,缓存的数据就会占用大量内存空间因此我们在实际开发中,不要像上面案例那样有意将报文拆开来发送。
-
此外如果client发送了半包,而剩余部分内容没有发送就关闭了對于这种情况,netty服务端在销毁连接时会自动清空之前缓存的数据,不会一直缓存