NIO:与 Buffer 一起使用 Channel
如前文所述,Channel實(shí)例代表了一個(gè)與設(shè)備的連接,通過(guò)它可以進(jìn)行輸入輸出操作。實(shí)際上Channel的基本思想與我們見(jiàn)過(guò)的普通套接字非常相似。對(duì)于TCP協(xié)議,可以使用ServerSocketChannel和SocketChannel。還有一些針對(duì)其他設(shè)備的其他類(lèi)型信道(如,FileChannel),盡管我們?cè)诤笪闹胁粫?huì)再提及,這里介紹的大部分內(nèi)容對(duì)于它們同樣適用。信道(channel)和套接字(socket)之間的不同點(diǎn)之一,可能是信道通常要調(diào)用靜態(tài)工廠方法來(lái)獲取實(shí)例:
SocketChannel clntChan = SocketChannel.open();
ServerSocketChannel servChan =
ServerSocketChannel.open();
?Channel使用的不是流,而是緩沖區(qū)來(lái)發(fā)送或讀取數(shù)據(jù)。Buffer類(lèi)或其任何子類(lèi)的實(shí)例都可以看作是一個(gè)定長(zhǎng)的Java基本數(shù)據(jù)類(lèi)型元素序列。與流不同,緩沖區(qū)有固定的、有限的容量,并由內(nèi)部(但可以被訪問(wèn))狀態(tài)記錄了有多少數(shù)據(jù)放入或取出,就像是有限容量的隊(duì)列一樣。Buffer是一個(gè)抽象類(lèi),只能通過(guò)創(chuàng)建它的子類(lèi)來(lái)獲得Buffer實(shí)例,而每個(gè)子類(lèi)都設(shè)計(jì)為用來(lái)容納一種Java基本數(shù)據(jù)類(lèi)型(boolean除外)。因此,這些實(shí)例分別為FloatBuffer,或IntBuffer,或ByteBuffer,等等(ByteBuffer是這些實(shí)例中最靈活的,并將在后面很多例子中用到)。在channel中使用Buffer實(shí)例通常不是使用構(gòu)造函數(shù)創(chuàng)建的,而是通過(guò)調(diào)用allocate()方法創(chuàng)建指定容量的Buffer實(shí)例,
ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);
或通過(guò)包裝一個(gè)已有的數(shù)組來(lái)創(chuàng)建:
ByteBuffer buffer = ByteBuffer.wrap(byteArray);?
?
NIO的強(qiáng)大功能部分來(lái)自于channel的非阻塞特性。回顧前面介紹的內(nèi)容可以知道,套接字的某些操作可能會(huì)無(wú)限期地阻塞。例如,對(duì)accept()方法的調(diào)用可能會(huì)因?yàn)榈却粋€(gè)客戶端連接而阻塞;對(duì)read()方法的調(diào)用可能會(huì)因?yàn)闆](méi)有數(shù)據(jù)可讀而阻塞,直到連接的另一端傳來(lái)新的數(shù)據(jù)??偟膩?lái)說(shuō),創(chuàng)建/接收連接或讀寫(xiě)數(shù)據(jù)等I/O調(diào)用,都可能無(wú)限期地阻塞等待,直到底層的網(wǎng)絡(luò)實(shí)現(xiàn)發(fā)生了什么。慢速的、有損耗的網(wǎng)絡(luò),或僅僅是簡(jiǎn)單的網(wǎng)絡(luò)故障都可能導(dǎo)致任意時(shí)間的延遲。然而不幸的是,在調(diào)用一個(gè)方法之前無(wú)法知道其是否會(huì)阻塞。NIO的channel抽象的一個(gè)重要特征就是可以通過(guò)配置它的阻塞行為,以實(shí)現(xiàn)非阻塞式的信道。
clntChan.configureBlocking(false);
?
在非阻塞式信道上調(diào)用一個(gè)方法總是會(huì)立即返回。這種調(diào)用的返回值指示了所請(qǐng)求的操作完成的程度。例如,在一個(gè)非阻塞式ServerSocketChannel上調(diào)用accept()方法,如果有連接請(qǐng)求在等待,則返回客戶端SocketChannel,否則返回null。下面我們來(lái)創(chuàng)建一個(gè)非阻塞式TCP回顯客戶端??赡茏枞?/span>I/O操作包括建立連接,讀和寫(xiě)。通過(guò)使用非阻塞式信道,這些操作都將立即返回。我們必須反復(fù)調(diào)用這些操作,直到所有I/O操作都成功完成。
?
TCPEchoClientNonblocking.java
0 import java.net.InetSocketAddress;
1 import java.net.SocketException;
2 import java.nio.ByteBuffer;
3 import java.nio.channels.SocketChannel;
4
5 public class TCPEchoClientNonblocking {
6
7 public static void main(String args[]) throws Exception
{
8
9 if ((args.length < 2) || (args.length > 3)) // Test for
correct # of args
10 throw new IllegalArgumentException("Parameter(s):
<Server> <Word> [<Port>]");
11
12 String server = args[0]; // Server name or IP address
13 // Convert input String to bytes using the default
charset
14 byte[] argument = args[1].getBytes();
15
16 int servPort = (args.length == 3) ?
Integer.parseInt(args[2]) : 7;
17
18 // Create channel and set to nonblocking
19 SocketChannel clntChan = SocketChannel.open();
20 clntChan.configureBlocking(false);
21
22 // Initiate connection to server and repeatedly poll
until complete
23 if (!clntChan.connect(new InetSocketAddress(server,
servPort))) {
24 while (!clntChan.finishConnect()) {
25 System.out.print("."); // Do something else
26 }
27 }
28 ByteBuffer writeBuf = ByteBuffer.wrap(argument);
29 ByteBuffer readBuf =
ByteBuffer.allocate(argument.length);
30 int totalBytesRcvd = 0; // Total bytes received so far
31 int bytesRcvd; // Bytes received in last read
32 while (totalBytesRcvd < argument.length) {
33 if (writeBuf.hasRemaining()) {
34 clntChan.write(writeBuf);
35 }
36 if ((bytesRcvd = clntChan.read(readBuf)) == -1) {
37 throw new SocketException("Connection closed
prematurely");
38 }
39 totalBytesRcvd += bytesRcvd;
40 System.out.print("."); // Do something else
41 }
42
43 System.out.println("Received: " + // convert to String
per default charset
44 new String(readBuf.array(), 0, totalBytesRcvd));
45 clntChan.close();
46 }
47 }
?
TCPEchoClientNonblocking.java
?1.獲取并轉(zhuǎn)換參數(shù):第9-16行
2.?創(chuàng)建非阻塞式SocketChannel:第19-20行
3.連接服務(wù)器:第23-27行?
由于該套接字是非阻塞式的,因此對(duì)connect()方法的調(diào)用可能會(huì)在連接建立之前返回,如果在返回前已經(jīng)成功建立了連接,則返回true,否則返回false。對(duì)于后一種情況,任何試圖發(fā)送或接收數(shù)據(jù)的操作都將拋出NotYetConnectedException異常,因此,我們通過(guò)持續(xù)調(diào)用finishConnect()方法來(lái)"輪詢"連接狀態(tài),該方法在連接成功建立之前一直返回false。打印操作演示了在等待連接建立的過(guò)程中,程序還可以執(zhí)行其他任務(wù)。不過(guò),這種忙等的方法非常浪費(fèi)系統(tǒng)資源,這里這樣做只是為了演示該方法的使用。?
4.創(chuàng)建讀寫(xiě)緩沖區(qū):第28-29行
?我們分別使用了兩種方法來(lái)創(chuàng)建將要用來(lái)讀寫(xiě)數(shù)據(jù)的ByteBuffer實(shí)例。一是通過(guò)包裝包含了要發(fā)送數(shù)據(jù)的byte[]數(shù)組,另一個(gè)方法是調(diào)用allocate()方法,創(chuàng)建具有與前面byte[]數(shù)組大小相同緩沖區(qū)的ByteBuffer實(shí)例。
?5.反復(fù)循環(huán)直到發(fā)送和接收完所有字節(jié):第32-41行
只要輸出緩沖區(qū)中還留有數(shù)據(jù),就調(diào)用write()方法。對(duì)read()方法的調(diào)用不會(huì)阻塞等待,但是當(dāng)沒(méi)有數(shù)據(jù)可讀時(shí)該方法將返回0。這里,打印語(yǔ)句再次舉例說(shuō)明了在等待通信完成的過(guò)程中,程序可以執(zhí)行其他任務(wù)。
6.打印接收到的數(shù)據(jù):第43-44行
7.關(guān)閉信道:第45行
與套接字類(lèi)似,信道在完成其任務(wù)后也需要關(guān)閉。
?
相關(guān)下載:
Java_TCPIP_Socket編程(doc)
http://download.csdn.net/detail/undoner/4940239
?
文獻(xiàn)來(lái)源:
UNDONER(小杰博客) :http://blog.csdn.net/undoner
LSOFT.CN(瑯軟中國(guó)) :http://www.lsoft.cn
?
轉(zhuǎn)載于:https://www.cnblogs.com/wuyida/archive/2012/12/29/6301059.html
總結(jié)
以上是生活随笔為你收集整理的NIO:与 Buffer 一起使用 Channel的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 生成短GUID
- 下一篇: [leetcode]Divide Two