前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty 入门

Netty 入门

作者头像
lwen
发布2018-04-17 16:43:36
8470
发布2018-04-17 16:43:36
举报
文章被收录于专栏:Java 源码分析Java 源码分析

1. 粘包问题

一 .长连接与短连接:

1.长连接:Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收。长连接在 netty 中是默认开启的,也就是我们创建了一个 Server 以后监听端口,我们的客户端去连接发现只要我们的客户端不主动的断开连接他们之间的连接是一直保持有效的。 2.短连接:Client方与Server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此种方式常用于一点对多点通讯,比如多个Client连接一个Server。但是在 netty 中默认采用了长连接,我们如何使用短连接呢?其实很简单,在我们的 Server 端需要对客户端进行回写数据的时候我们只需要在回写的后面加上一个监听事件,就是当写完毕,我们就关闭此连接。

代码语言:javascript
复制
ctx.channel().writeAndFlush("Hi Client I'm Server !&&").addListener(ChannelFutureListener.CLOSE);

二 .什么时候需要考虑粘包问题?

1:如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题,因为只有一种包结构,类似于http协议。 2:如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包 3:如果双方建立连接,需要在连接后一段时间内发送不同结构数据,则需要考虑粘包问题。

三 .粘包出现原因

TCP 是一个字节流的的传输,也就是在流中传输无固定结构的数据包。但是UDP不会出现粘包,因为它有消息边界。 1 发送端需要等缓冲区满才发送出去,造成粘包 2 接收方不及时接收缓冲区的包,造成多个包接收

具体的在 TCP 中出现这种情况的原因:

  1. 由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法.简单的说,当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间,看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去.这是对Nagle算法一个简单的解释,详细的请看相关书籍.
  2. 接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据.当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据. 四 .解决办法 为了避免粘包现象,可采取以下几种措施:
  3. 一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
  4. 二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
  5. 三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

   以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

   最初遇到”粘包”的问题时,我是通过在两次send之间调用sleep来休眠一小段时间来解决.这个解决方法的缺点是显而易见的,使传输效率大大降低,而且也并不可靠.后来就是通过应答的方式来解决,尽管在大多数时候是可行的,但是不能解决象B的那种情况,而且采用应答方式增加了通讯量,加重了网络负荷. 再后来就是对数据包进行封包和拆包的操作.    封包:封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了(以后讲过滤非法包时封包会加入”包尾”内容).包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,这是个很重要的变量,其他的结构体成员可根据需要自己定义.根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包.

五 .在 netty 中解决粘包的方式

  1. 采用了分隔符,类似于链路层的那种使用一个特殊的标记来分割数据,这里主要采用了一个工具类 DelimiterBasedFrameDecoder(arg0,agr1) 其中第一个参数指的是这个分隔符的可占用的空间大小,只能比他大不能小于。第二个参数就是具体的分隔符了,但是不能传递一个字符串而是一个 ByteBuf 对象,也就是我们需要用工具转一下 Unpooled.copiedBuffer("&&".getBytes()) ,代码如下:
代码语言:javascript
复制
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("&&".getBytes())));
  1. 第二种方式也比较简单,这个就是采用了定长的报文,当我们的报文长度不够的时候必须要采用空格补齐。比如说,我们发送了 “aaaaacc” 而我们的定长指定的是 5 那么,我们只会收到 “aaaaa” 而没有 c 这就是因为他不足 5 个,我们必须采用空格补齐才能收到,或者等待下次数据比较多的时候会跟着过来。
代码语言:javascript
复制
.addLast(new FixedLengthFrameDecoder(5));
  1. pojo 方式,也就是自定义数据的头和体部分,其中头中最重要的就是一个长度字段。

参考资料:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 粘包问题
    • 一 .长连接与短连接:
      • 二 .什么时候需要考虑粘包问题?
        • 三 .粘包出现原因
          • 五 .在 netty 中解决粘包的方式
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档