Java NIO框架Netty教程(四) ChannelBuffer
在學字符串消息收發(http://www.it165.net/pro/html/201207/3174.html)的時候,已經提到過。ChannelBuffer是Netty中非常重要的概念。所有消息的收發都依賴於這個Buffer。我們通過Netty的官方的文檔來了解一下,基於流的消息傳遞機制。
In a stream-based transport such as TCP/IP, received data is stored into a socket receive buffer.
Unfortunately, the buffer of a stream-based transport is not a queue of packets but a queue of bytes. It
means, even if you sent two messages as two independent packets, an operating system will not treat them
as two messages but as just a bunch of bytes. Therefore, there is no guarantee that what you read is exactly
what your remote peer wrote. For example, let us assume that the TCP/IP stack of an operating system has
received three packets:
+—–+—–+—–+
| ABC | DEF | GHI |
+—–+—–+—–+
Because of this general property of a stream-based protocol, there‘s high chance of reading them in the
following fragmented form in your application:
+—-+——-+—+—+
| AB | CDEFG | H | I |
+—-+——-+—+—+
Therefore, a receiving part, regardless it is server-side or client-side, should defrag the received data into one
or more meaningful frames that could be easily understood by the application logic. In case of the example
above, the received data should be framed like the following:
+—–+—–+—–+
| ABC | DEF | GHI |
+—–+—–+—–+
不知道您理解了沒,簡單翻譯一下就是說。在TCP/IP這種基於流傳遞的協議中。他識別的不是你每一次發送來的消息,不是分包的。而是,只認識一個整體的流,即使分三次分別發送三段話:ABC、DEF、GHI。在傳遞的過程中,他就是一個具有整體長度的流。在讀流的過程中,如果我一次讀取的長度選擇的不是三個,我可以收到類似AB、CDEFG、H、I這樣的信息。這顯然是我們不想看到的。所以說,在你寫的消息收發的系統裏,需要預先定義好這種解析機制,規定每幀(次)讀取的長度。通過代碼來理解一下:
view sourceprint?
01.
/**
02.
* @author lihzh
03.
* @alia OneCoder
04.
* @blog http://www.it165.net
05.
*/
06.
public
class
ServerBufferHandler
extends
SimpleChannelHandler {
07.
08.
/**
09.
* 用戶接受客戶端發來的消息,在有客戶端消息到達時觸發
10.
*
11.
* @author lihzh
12.
* @alia OneCoder
13.
*/
14.
@Override
15.
public
void
messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
16.
ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
17.
// 五位讀取
18.
while
(buffer.readableBytes() >=
5
) {
19.
ChannelBuffer tempBuffer = buffer.readBytes(
5
);
20.
System.out.println(tempBuffer.toString(Charset.defaultCharset()));
21.
}
22.
// 讀取剩下的信息
23.
System.out.println(buffer.toString(Charset.defaultCharset()));
24.
}
25.
26.
}
view sourceprint?
01.
/**
02.
* @author lihzh
03.
* @alia OneCoder
04.
* @blog http://www.it165.net
05.
*/
06.
public
class
ClientBufferHandler
extends
SimpleChannelHandler {
07.
08.
/**
09.
* 當綁定到服務端的時候觸發,給服務端發消息。
10.
*
11.
* @alia OneCoder
12.
* @author lihzh
13.
*/
14.
@Override
15.
public
void
channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
16.
// 分段發送信息
17.
sendMessageByFrame(e);
18.
}
19.
20.
/**
21.
* 將<b>"Hello, I‘m client."</b>分成三份發送。 <br>
22.
* Hello, <br>
23.
* I‘m<br>
24.
* client.<br>
25.
*
26.
* @param e
27.
* Netty事件
28.
* @author lihzh
29.
*/
30.
private
void
sendMessageByFrame(ChannelStateEvent e) {
31.
String msgOne =
"Hello, "
;
32.
String msgTwo =
"I‘m "
;
33.
String msgThree =
"client."
;
34.
e.getChannel().write(tranStr2Buffer(msgOne));
35.
e.getChannel().write(tranStr2Buffer(msgTwo));
36.
e.getChannel().write(tranStr2Buffer(msgThree));
37.
}
38.
39.
/**
40.
* 將字符串轉換成{@link ChannelBuffer},私有方法不進行字符串的非空判斷。
41.
*
42.
* @param str
43.
* 待轉換字符串,要求非null
44.
* @return 轉換後的ChannelBuffer
45.
* @author lihzh
46.
*/
47.
private
ChannelBuffer tranStr2Buffer(String str) {
48.
ChannelBuffer buffer = ChannelBuffers.buffer(str.length());
49.
buffer.writeBytes(str.getBytes());
50.
return
buffer;
51.
}
52.
53.
}
服務端輸出結果:
Hello
, I‘m
clie
nt.
這裏其實,服務端是否分段發送並不會影響輸出結果,也就是說,你一次性的把"Hi, I‘m client."這段信息發送過來,輸出的結果也是一樣的。這就是開頭說的,傳輸的是流,不分包。而只在於你如何分段讀寫。
Java NIO框架Netty教程(四) ChannelBuffer