netty学习笔记:从jdk NIO到netty作用

之前一篇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协议是基于数据流(就是没有界限没有分割的一串数据))

UDP为啥没有这个问题。UDP本身作为无连接的不可靠的传输协议(适合频繁发送较小的数据包),他不会对数据包进行合并发送,他直接是一端发送什么数据,直接就发出去了,既然他不会对数据合并,每一个数据包都是完整的。即使分包后如下case,网络数据经过路由器,如果数据很小,没有超过路由器的封包大小,就会直接直接经过路由器到达下一个路由器,一层一层最终到达目的地如果数据很大,这里指一个发送,超过了路由器的封包大小,那么路由器就会把这个数据包进行拆分,比如拆分成A B C三个包,这三个包都没有超过路由器的封包大小,到达下一个路由器的时候,TCP与UDP的区别就来了:
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

你可能感兴趣的:(网络)