netty 学习 FrameDecoder

我们接下来就看和业务息息相关的解码器,首先我们来看FrameDecoder,这个东西应该是所有的解码器都会实现这个,所以我们来重点看一下。

FrameDecoder产生的根源就是TCP/IP数据包的传输方式决定的,包在传输的过程中会分片和重组,

正如javadoc里面所说的:

客户端在发送的时候的序列如下:

+-----+-----+-----+

| ABC | DEF | GHI |

+-----+-----+-----+

服务器端在接受到后可能会变成下面的序列:

+----+-------+---+---+

| AB | CDEFG | H | I |

+----+-------+---+---+

FrameDecoder帮助我们将接受到的数据包整理成有意义的数据帧,例如,可以帮助我们将数据包整理成

下面的数据格式:

+-----+-----+-----+

| ABC | DEF | GHI |

+-----+-----+-----+

我们接下来就看看FrameDecoder的实现吧:

FrameDecoder是继承SimpleChannelUpstreamHandler的,我们首先来看一下messageReceived的实现:

Java代码  

  1. @Override
  2. public void messageReceived(
  3. ChannelHandlerContext ctx, MessageEvent e) throws Exception {
  4. Object m = e.getMessage();
  5. if (!(m instanceof ChannelBuffer)) {
  6. ctx.sendUpstream(e);
  7. return;
  8. }
  9. ChannelBuffer input = (ChannelBuffer) m;
  10. if (!input.readable()) {
  11. return;
  12. }
  13. ChannelBuffer cumulation = cumulation(ctx);
  14. if (cumulation.readable()) {
  15. cumulation.discardReadBytes();
  16. cumulation.writeBytes(input);
  17. callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress());
  18. } else {
  19. callDecode(ctx, e.getChannel(), input, e.getRemoteAddress());
  20. if (input.readable()) {
  21. cumulation.writeBytes(input);
  22. }
  23. }
  24. }

这个里面首先会检查input的可读性,这个比较好理解,关键是cumulation,

我们首先来看一下cumulation的实现吧:

Java代码  

  1. private ChannelBuffer cumulation(ChannelHandlerContext ctx) {
  2. ChannelBuffer c = cumulation;
  3. if (c == null) {
  4. c = ChannelBuffers.dynamicBuffer(
  5. ctx.getChannel().getConfig().getBufferFactory());
  6. cumulation = c;
  7. }
  8. return c;
  9. }

这个函数很简单,就是如果cumulation为空的时候初始化一下,如果不为空,就返回。我们得思考一下什么时候cumulation为空,什么时候不为空。我们再回过头来看一下上面的实现吧。如果cumulation可读,cumulation.discardReadBytes函数的作用是将0到readIndex之间的空间释放掉,将readIndex和writeIndex都重新标记一下。然后将读到的数据写到buffer里面。如果cumulation不可读,在调callDecode,如果发现从不可读状态到可读状态,则将读到的数据写到缓存区里面。

我们再来看callDecode的实现:

Java代码  

  1. private void callDecode(
  2. ChannelHandlerContext context, Channel channel,
  3. ChannelBuffer cumulation, SocketAddress remoteAddress) throws Exception {
  4. while (cumulation.readable()) {
  5. int oldReaderIndex = cumulation.readerIndex();
  6. Object frame = decode(context, channel, cumulation);
  7. if (frame == null) {
  8. if (oldReaderIndex == cumulation.readerIndex()) {
  9. // Seems like more data is required.
  10. // Let us wait for the next notification.
  11. break;
  12. } else {
  13. // Previous data has been discarded.
  14. // Probably it is reading on.
  15. continue;
  16. }
  17. } else if (oldReaderIndex == cumulation.readerIndex()) {
  18. throw new IllegalStateException(
  19. "decode() method must read at least one byte " +
  20. "if it returned a frame (caused by: " + getClass() + ")");
  21. }
  22. unfoldAndFireMessageReceived(context, remoteAddress, frame);
  23. }
  24. if (!cumulation.readable()) {
  25. this.cumulation = null;
  26. }
  27. }

这个里面上面是一个循环,首先将读指针备份一下,decode方法是交个子类实现的一个抽象方这个用来实现具体数据分帧的算法,从这个里面看到如果子类没有读到一帧数据,则返回null所以下面有一个判断,是一点数据没有读呢,还是读了一点,如果一点都没有读,就不需要再检测了等下一次messageRecieved进行通知,如果发现读了一点数据,就调用下一次分帧。如果读了一帧数据就发送一个通知,unfold是针对读到的循环数据要不要打开的意思。到最后如果发现不是可读状态,

cumulation将会被设置成null。

最后来看一下cleanup的实现

Java代码  

  1. private void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e)
  2. throws Exception {
  3. try {
  4. ChannelBuffer cumulation = this.cumulation;
  5. if (cumulation == null) {
  6. return;
  7. } else {
  8. this.cumulation = null;
  9. }
  10. if (cumulation.readable()) {
  11. // Make sure all frames are read before notifying a closed channel.
  12. callDecode(ctx, ctx.getChannel(), cumulation, null);
  13. }
  14. // Call decodeLast() finally.  Please note that decodeLast() is
  15. // called even if there‘s nothing more to read from the buffer to
  16. // notify a user that the connection was closed explicitly.
  17. Object partialFrame = decodeLast(ctx, ctx.getChannel(), cumulation);
  18. if (partialFrame != null) {
  19. unfoldAndFireMessageReceived(ctx, null, partialFrame);
  20. }
  21. } finally {
  22. ctx.sendUpstream(e);
  23. }
  24. }

在这个里面一般来说是在socket断开的时候调用,这个时候如果发现buffer还是可读状态,还会努力的确保所有的数据已经被分帧,然后调用decodeLast

===========================================================================================

转自http://blog.csdn.net/xiaolang85/article/details/12621663

时间: 2024-08-24 17:35:14

netty 学习 FrameDecoder的相关文章

netty 学习资料

最近在做一个网页遥控器的项目,用到了netty,但还未发现比较系统完整的netty博客教程,所以打算自己写一个netty学习教程,会每天更新一篇,欢迎交流. 先给大家提供一些资料: 1. 比较简短易懂的有实例的系列教程,涉及到了netty关键特性:但个人觉得比较速成,不系统,不深入 http://www.coderli.com/netty-course-hello-world http://www.coderli.com/netty-two-concepts http://www.coderli

netty学习资源收集

Netty学习笔记 Netty+In+Action中文版.pdf

Netty学习篇--整合springboot

经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合) 项目准备 工具:IDEA2017 jar包导入:maven 项目框架:springboot+netty 项目操作 右键创建一个maven项目,项目名称: hetangyuese-netty-03(项目已上传github) 项目完整结构 ? maven导包 <!-- netty start --> <dependency> <groupId>

Netty学习——基于netty实现简单的客户端聊天小程序

Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEven

Netty学习——通过websocket编程实现基于长连接的双攻的通信

Netty学习(一)基于长连接的双攻的通信,通过websocket编程实现 效果图,客户端和服务器端建立起长连接,客户端发送请求,服务器端响应 但是目前缺少心跳,如果两个建立起来的连接,一个断网之后,另外一个是感知不到对方已经断掉的.以后使用心跳技术来进行连接检测 须知: 状态码101,代表 协议转换,从HTTP协议升级为WebSocket协议 HTTP协议,一般访问的时候:是 Http://localhost:8080/ws WebSocket协议,访问的时候,需要是:ws://localho

Netty学习——Netty和Protobuf的整合(一)

Netty学习——Netty和Protobuf的整合 Protobuf作为序列化的工具,将序列化后的数据,通过Netty来进行在网络上的传输 1.将proto文件里的java包的位置修改一下,然后再执行一下protoc 异常捕获:启动服务器端正常,在启动客户端的时候,发送消息,报错 警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the l

netty学习:UDP服务器与Spring整合(2)

上一篇文章中,介绍了netty实现UDP服务器的栗子,本文将会对UDP服务器与spring boot整合起来,并使用RedisTemplate的操作类访问Redis和使用JPA链接MySQL,其中会使用多线程.异步等知识. 本人使用的编辑器是IntelliJ IDEA 2017.1.exe版本(链接:http://pan.baidu.com/s/1pLODHm7 密码:dlx7):建议使用STS或者是idea编辑器来进行spring的学习. 1)项目目录结构 整个项目的目录结构如下: 2)jar

(二)Netty学习笔记之服务端启动

本文将不会对netty中每个点分类讲解,而是一个服务端启动的代码走读,在这个过程中再去了解和学习,这也是博主自己的学习历程.下面开始正文~~~~ 众所周知,在写netty服务端应用的时候一般会有这样的启动代码: (代码一) 1 EventLoopGroup bossGroup = new NioEventLoopGroup(1); 2 EventLoopGroup workerGroup = new NioEventLoopGroup(); 3 try { 4 ServerBootstrap b

netty 学习记录一

最近在学习netty相关知识,觉得<netty 权威指南>这本书还是挺好的,适合我这种初学者.加上netty本身自带的许多例子,学起来还是挺有兴趣的.简单记录下, 一般服务器代码如下: public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerB