Java NIO框架Netty教程(十) Object对象的连续收发解析分析

来源:互联网 发布:80端口入侵工具 编辑:程序博客网 时间:2024/04/28 06:01

如果您一直关注OneCoder,我们之前有两篇文章介绍关于Netty消息连续收发的问题。( 《Java NIO框架Netty教程(五) 消息收发次数不匹配的问题 》、《 Java NIO框架Netty教程(七)-再谈收发信息次数问题 》)。如果您经常的“怀疑”和思考,我们刚介绍过了Object的传递,您是否好奇,在Object传递中是否会有这样的问题?如果Object流的字节截断错乱,那肯定是会出错的。Netty一定不会这么傻的,那么Netty是怎么做的呢?

我们先通过代码验证一下是否有这样的问题。(有问题的可能性几乎没有。)

 

view sourceprint?
01./**
02.* 当绑定到服务端的时候触发,给服务端发消息。
03.*
04.* @author lihzh
05.* @alia OneCoder
06.*/www.it165.net
07.@Override
08.public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
09.// 向服务端发送Object信息
10.sendObject(e.getChannel());
11.}
12. 
13./**
14.* 发送Object
15.*
16.* @param channel
17.* @author lihzh
18.* @alia OneCoder
19.*/
20.private void sendObject(Channel channel) {
21.Command command = new Command();
22.command.setActionName("Hello action.");
23.Command commandOne = new Command();
24.commandOne.setActionName("Hello action. One");
25.Command command2 = new Command();
26.command2.setActionName("Hello action. Two");
27.channel.write(command2);
28.channel.write(command);
29.channel.write(commandOne);
30.}

打印结果:

Hello action. Two
Hello action.
Hello action. One

一切正常。那么Netty是怎么分割对象流的呢?看看ObjectDecoder怎么做的。
在ObjectDecoder的基类LengthFieldBasedFrameDecoder中注释中有详细的说明。我们这里主要介绍一下关键的代码逻辑:

 

view sourceprint?
01.@Override
02.protected Object decode(
03.ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
04. 
05.if (discardingTooLongFrame) {
06.long bytesToDiscard = this.bytesToDiscard;
07.int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes());
08.buffer.skipBytes(localBytesToDiscard);
09.bytesToDiscard -= localBytesToDiscard;
10.this.bytesToDiscard = bytesToDiscard;
11.failIfNecessary(ctx, false);
12.return null;
13.}
14. 
15.if (buffer.readableBytes() < lengthFieldEndOffset) {
16.return null;
17.}
18. 
19.int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset;
20.long frameLength;
21.switch (lengthFieldLength) {
22.case 1:
23.frameLength = buffer.getUnsignedByte(actualLengthFieldOffset);
24.break;
25.case 2:
26.frameLength = buffer.getUnsignedShort(actualLengthFieldOffset);
27.break;
28.case 3:
29.frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset);
30.break;
31.case 4:
32.frameLength = buffer.getUnsignedInt(actualLengthFieldOffset);
33.break;
34.……

我们这里进入的是4,还记得在编码时候的开头的4位占位字节吗?跟踪进去发现。


view sourceprint?
1.public int getInt(int index) {
2.return  (array[index]     & 0xff) << 24 |
3.(array[index + 1] & 0xff) << 16 |
4.(array[index + 2] & 0xff) <<  8 |
5.(array[index + 3] & 0xff) <<  0;
6.}

原来,当初在编码时,在流开头增加的4字节的字符是做这个的。他记录了当前了这个对象流的长度,便于在解码时候准确的计算出该对象流的长度,正确解码。看来,我们如果我们自己写的对象编码解码的工具,要考虑的还有很多啊。

附:LengthFieldBasedFrameDecoder的JavaDoc

 

view sourceprint?
001./**
002.* A decoder that splits the received {@link ChannelBuffer}s dynamically by the
003.* value of the length field in the message.  It is particularly useful when you
004.* decode a binary message which has an integer header field that represents the
005.* length of the message body or the whole message.
006.* <p>
007.* {@link LengthFieldBasedFrameDecoder} has many configuration parameters so
008.* that it can decode any message with a length field, which is often seen in
009.* proprietary client-server protocols. Here are some example that will give
010.* you the basic idea on which option does what.
011.*
012.* <h3>2 bytes length field at offset 0, do not strip header</h3>
013.*
014.* The value of the length field in this example is <tt>12 (0x0C)</tt> which
015.* represents the length of "HELLO, WORLD".  By default, the decoder assumes
016.* that the length field represents the number of the bytes that follows the
017.* length field.  Therefore, it can be decoded with the simplistic parameter
018.* combination.
019.* <pre>
020.* <b>lengthFieldOffset</b>   = <b>0</b>
021.* <b>lengthFieldLength</b>   = <b>2</b>
022.* lengthAdjustment    = 0
023.* initialBytesToStrip = 0 (= do not strip header)
024.*
025.* BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
026.* +——–+—————-+      +——–+—————-+
027.* | Length | Actual Content |—–>| Length | Actual Content |
028.* | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
029.* +——–+—————-+      +——–+—————-+
030.* </pre>
031.*
032.* <h3>2 bytes length field at offset 0, strip header</h3>
033.*
034.* Because we can get the length of the content by calling
035.* {@link ChannelBuffer#readableBytes()}, you might want to strip the length
036.* field by specifying <tt>initialBytesToStrip</tt>.  In this example, we
037.* specified <tt>2</tt>, that is same with the length of the length field, to
038.* strip the first two bytes.
039.* <pre>
040.* lengthFieldOffset   = 0
041.* lengthFieldLength   = 2
042.* lengthAdjustment    = 0
043.* <b>initialBytesToStrip</b> = <b>2</b> (= the length of the Length field)
044.*
045.* BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
046.* +——–+—————-+      +—————-+
047.* | Length | Actual Content |—–>| Actual Content |
048.* | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
049.* +——–+—————-+      +—————-+
050.* </pre>
051.*
052.* <h3>2 bytes length field at offset 0, do not strip header, the length field
053.*     represents the length of the whole message</h3>
054.*
055.* In most cases, the length field represents the length of the message body
056.* only, as shown in the previous examples.  However, in some protocols, the
057.* length field represents the length of the whole message, including the
058.* message header.  In such a case, we specify a non-zero
059.* <tt>lengthAdjustment</tt>.  Because the length value in this example message
060.* is always greater than the body length by <tt>2</tt>, we specify <tt>-2</tt>
061.* as <tt>lengthAdjustment</tt> for compensation.
062.* <pre>
063.* lengthFieldOffset   =  0
064.* lengthFieldLength   =  2
065.* <b>lengthAdjustment</b>    = <b>-2</b> (= the length of the Length field)
066.* initialBytesToStrip =  0
067.*
068.* BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
069.* +——–+—————-+      +——–+—————-+
070.* | Length | Actual Content |—–>| Length | Actual Content |
071.* | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
072.* +——–+—————-+      +——–+—————-+
073.* </pre>
074.*
075.* <h3>3 bytes length field at the end of 5 bytes header, do not strip header</h3>
076.*
077.* The following message is a simple variation of the first example.  An extra
078.* header value is prepended to the message.  <tt>lengthAdjustment</tt> is zero
079.* again because the decoder always takes the length of the prepended data into
080.* account during frame length calculation.
081.* <pre>
082.* <b>lengthFieldOffset</b>   = <b>2</b> (= the length of Header 1)
083.* <b>lengthFieldLength</b>   = <b>3</b>
084.* lengthAdjustment    = 0
085.* initialBytesToStrip = 0
086.*
087.* BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
088.* +———-+———-+—————-+      +———-+———-+—————-+
089.* | Header 1 |  Length  | Actual Content |—–>| Header 1 |  Length  | Actual Content |
090.* |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
091.* +———-+———-+—————-+      +———-+———-+—————-+
092.* </pre>
093.*
094.* <h3>3 bytes length field at the beginning of 5 bytes header, do not strip header</h3>
095.*
096.* This is an advanced example that shows the case where there is an extra
097.* header between the length field and the message body.  You have to specify a
098.* positive <tt>lengthAdjustment</tt> so that the decoder counts the extra
099.* header into the frame length calculation.
100.* <pre>
101.* lengthFieldOffset   = 0
102.* lengthFieldLength   = 3
103.* <b>lengthAdjustment</b>    = <b>2</b> (= the length of Header 1)
104.* initialBytesToStrip = 0
105.*
106.* BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
107.* +———-+———-+—————-+      +———-+———-+—————-+
108.* |  Length  | Header 1 | Actual Content |—–>|  Length  | Header 1 | Actual Content |
109.* | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
110.* +———-+———-+—————-+      +———-+———-+—————-+
111.* </pre>
112.*
113.* <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
114.*     strip the first header field and the length field</h3>
115.*
116.* This is a combination of all the examples above.  There are the prepended
117.* header before the length field and the extra header after the length field.
118.* The prepended header affects the <tt>lengthFieldOffset</tt> and the extra
119.* header affects the <tt>lengthAdjustment</tt>.  We also specified a non-zero
120.* <tt>initialBytesToStrip</tt> to strip the length field and the prepended
121.* header from the frame.  If you don't want to strip the prepended header, you
122.* could specify <tt>0</tt> for <tt>initialBytesToSkip</tt>.
123.* <pre>
124.* lengthFieldOffset   = 1 (= the length of HDR1)
125.* lengthFieldLength   = 2
126.* <b>lengthAdjustment</b>    = <b>1</b> (= the length of HDR2)
127.* <b>initialBytesToStrip</b> = <b>3</b> (= the length of HDR1 + LEN)
128.*
129.* BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
130.* +——+——–+——+—————-+      +——+—————-+
131.* | HDR1 | Length | HDR2 | Actual Content |—–>| HDR2 | Actual Content |
132.* | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
133.* +——+——–+——+—————-+      +——+—————-+
134.* </pre>
135.*
136.* <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
137.*     strip the first header field and the length field, the length field
138.*     represents the length of the whole message</h3>
139.*
140.* Let's give another twist to the previous example.  The only difference from
141.* the previous example is that the length field represents the length of the
142.* whole message instead of the message body, just like the third example.
143.* We have to count the length of HDR1 and Length into <tt>lengthAdjustment</tt>.
144.* Please note that we don't need to take the length of HDR2 into account
145.* because the length field already includes the whole header length.
146.* <pre>
147.* lengthFieldOffset   =  1
148.* lengthFieldLength   =  2
149.* <b>lengthAdjustment</b>    = <b>-3</b> (= the length of HDR1 + LEN, negative)
150.* <b>initialBytesToStrip</b> = <b> 3</b>
151.*
152.* BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
153.* +——+——–+——+—————-+      +——+—————-+
154.* | HDR1 | Length | HDR2 | Actual Content |—–>| HDR2 | Actual Content |
155.* | 0xCA | 0×0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
156.* +——+——–+——+—————-+      +——+—————-+
157.* </pre>
158.*
159.* @see LengthFieldPrepender
160.*/

0 0