netty里面FrameDecoder的理解

问题描述:

[color=blue][size=medium]在netty里面,messageReceived的实现如下:
[/size][/color][code="java"]
Object m = e.getMessage();
if (!(m instanceof ChannelBuffer)) {
ctx.sendUpstream(e);
return;
}

    ChannelBuffer input = (ChannelBuffer) m;
    if (!input.readable()) {
        return;
    }

    ChannelBuffer cumulation = cumulation(ctx);
    if (cumulation.readable()) {
        cumulation.discardReadBytes();
        cumulation.writeBytes(input);
        callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress());
    } else {
        callDecode(ctx, e.getChannel(), input, e.getRemoteAddress());
        if (input.readable()) {
            cumulation.writeBytes(input);
        }
    }

[/code]

[color=blue][size=medium]我们看到比较关键的是cumulation函数,函数如下:
[/size][/color][code="java"]
private ChannelBuffer cumulation(ChannelHandlerContext ctx) {
ChannelBuffer c = cumulation;
if (c == null) {
c = ChannelBuffers.dynamicBuffer(
ctx.getChannel().getConfig().getBufferFactory());
cumulation = c;
}
return c;
}
[/code]
[color=blue][size=medium]在这个里面为什么要这么实现呢,什么时候cumulation不为空呢?
我们再看看上面的实现:[/size][/color]
[code="java"]
if (cumulation.readable()) {
cumulation.discardReadBytes();
cumulation.writeBytes(input);
callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress());
} else {
callDecode(ctx, e.getChannel(), input, e.getRemoteAddress());
if (input.readable()) {
cumulation.writeBytes(input);
}
}
[/code]
[color=blue][size=medium]这个if和else里面的逻辑应该怎么理解。
[/size][/color]

1、什么时候cumulation不为空呢?
初次运行,cumulation == null,所以ChannelBuffer cumulation = cumulation(ctx);创建了一个空的dynamicBuffer;

cumulation.readable()是false,所以执行的是:
else {

callDecode(ctx, e.getChannel(), input, e.getRemoteAddress());

if (input.readable()) {

cumulation.writeBytes(input);

}

}

callDecode过程中,可能只decode了input的一部分,剩下的字节就写到cumulation,等到下次messageReceived之后,就看到cumulation不为空。(可能messageReceived一次接受了1.5个frame,callDecode只decode了一个frame,剩下的一半写入cumulation)

2、
接着上面,这时候cumulation.readable()是true,所以执行的是
if (cumulation.readable()) {

cumulation.discardReadBytes();

cumulation.writeBytes(input);

callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress());
}

这里将新接收到的input写入cumulation,然后继续decode。(又接收到了另一半,与先前接收到的合并到一起组成一个完整的frame)

3、源代码文档上说的很清楚了:
在像 TCP/IP这种基于流的传输过程中,即使在局域网环境中,报文也可能被分割和重组。FrameDecoder帮助我们将接受到的报文重新整理成有意义的、容易被应用程序逻辑理解的frames。

In a stream-based transport such as TCP/IP, packets can be fragmented and

  • reassembled during transmission even in a LAN environment. For example,
  • let us assume you have received three packets:
  • +-----+-----+-----+
  • | ABC | DEF | GHI |
  • +-----+-----+-----+
  • because of the packet fragmentation, a server can receive them like the
  • following:
  • +----+-------+---+---+
  • | AB | CDEFG | H | I |
  • +----+-------+---+---+ {@link FrameDecoder} helps you defrag the received packets into one or more
  • meaningful frames that could be easily understood by the
  • application logic. In case of the example above, your {@link FrameDecoder}
  • implementation could defrag the received packets like the following:
  • +-----+-----+-----+
  • | ABC | DEF | GHI |
  • +-----+-----+-----+

解码的一个关键就是处理tcp粘包的一个问题,netty将粘包的问题,自己处理了很多。
如果你用mina的话,需要自己去处理这些粘包的问题。看这段代码的话,可以很明白的。