您的位置:首页 > 移动开发 > Objective-C

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

2015-07-01 19:50 781 查看
如果您一直关注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. TwoHello 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的JavaDocview
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.
*/


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: