之前一篇Java网络I/O 介绍了Java I/O,从NIO引出了netty.
netty作用:
1 封装了I/O:
底层的I/O 实现复杂,netty屏蔽了底层。更面向业务层的实现。
2 对数据格式的封装
NIO只是封装了I/O模型,并不关心数据格式。而netty对数据格式的封装,更专注于业务。
支持常见的如pb,集成了HTTP 协议的request,response.
3 修复了JDK NIO层的已知bug.
4.解决了半包,粘包的问题
背景:
先说TCP:由于TCP协议本身的机制(面向连接的可靠地协议-三次握手机制)客户端与服务器会维持一个连接(Channel),数据在连接不断开的情况下,可以持续不断地将多个数据包发往服务器,但是如果发送的网络数据包太小,那么他本身会启用Nagle算法(可配置是否启用)对较小的数据包进行合并(基于此,TCP的网络延迟要UDP的高些)然后再发送(超时或者包大小足够)。那么这样的话,服务器在接收到消息(数据流)的时候就无法区分哪些数据包是客户端自己分开发送的,这样产生了粘包;服务器在接收到数据库后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现象(本质的原因,对于TCP协议是基于数据流(就是没有界限没有分割的一串数据))
TCP收到A的时候,会resp通知源路由器,A到达,B C包依然如此,如果由于网络的各种原因,目的路由收到了A C,B没有收到,TCP会要求源路由把B包重新发一次,直到ABC包目的路由都接受到了,那么目的路由把ABC包重新组成起始包,继续往下一个路由发送,这就是TCP安全连接的由来,只要发送,我就能保证目的一定能收到。
UDP则不是这样,如果ABC包拆分之后,目的路由只收到AC,经过检测,B没有被收到,那么此包就会被当作不完整,直接被丢弃。由于UDP没有resp的通知过程,所以,UDP的传输效率要高一些,当然安全性也低一些。所以UDP不存在粘包问题。
当然TCP短指令也不存在粘包问题,因为发送完一个指令链接就会断开,不会继续发送。
常见的解决措施是:
1 加入特殊分隔符。这样我们接收到数据后,如果出现结尾标识,即人为的将粘包分开,如果一个包中没有出现结尾符,认为出现了分包,则等待下个包中出现后 组合成一个完整的数据包,这种方式适合于文本传输的数如:"\r\n"等特殊字符。
2 数据包中添加长度的方式,即在数据包中的固定位置封装数据包的长度信息;例如:第一位代表封包头,第二位代表封类型,第三、四位代表封包的数据长度。然后后面是实际的数据内容。我们先把接收回来的数据写入一个流中。然后分析其中是否有完整的数据包,如果有,将其从流中取出,并将这部分数据从流中清除。直到流中没有完整的数据为止,以后接收回来的数据就将其写入流的结尾处,并从头继续分析。直到结束。
不管怎么办,在Java NIO里面,是需要自己动手去处理的。Netty 框架提供了许多解码器的封装,帮助我们解决半包粘包的问题。
FixedLengthFrameDecoder
定长解码器,需要客户端,服务端设置相同的frameLength:帧的固定长度
DelimiterBasedFrameDecoder
特殊字符解码器 需要约定特殊字符等等。
参考:
https://blog.csdn.net/cherish_2012/article/details/41681853