NIO:与 Buffer 一起使用 Channel
如前文所述,Channel實例代表了一個與設備的連接,通過它可以進行輸入輸出操作。實際上Channel的基本思想與我們見過的普通套接字非常相似。對于TCP協議,可以使用ServerSocketChannel和SocketChannel。還有一些針對其他設備的其他類型信道(如,FileChannel),盡管我們在后文中不會再提及,這里介紹的大部分內容對于它們同樣適用。信道(channel)和套接字(socket)之間的不同點之一,可能是信道通常要調用靜態工廠方法來獲取實例:
SocketChannel clntChan = SocketChannel.open();
ServerSocketChannel servChan =
ServerSocketChannel.open();
?Channel使用的不是流,而是緩沖區來發送或讀取數據。Buffer類或其任何子類的實例都可以看作是一個定長的Java基本數據類型元素序列。與流不同,緩沖區有固定的、有限的容量,并由內部(但可以被訪問)狀態記錄了有多少數據放入或取出,就像是有限容量的隊列一樣。Buffer是一個抽象類,只能通過創建它的子類來獲得Buffer實例,而每個子類都設計為用來容納一種Java基本數據類型(boolean除外)。因此,這些實例分別為FloatBuffer,或IntBuffer,或ByteBuffer,等等(ByteBuffer是這些實例中最靈活的,并將在后面很多例子中用到)。在channel中使用Buffer實例通常不是使用構造函數創建的,而是通過調用allocate()方法創建指定容量的Buffer實例,
ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);
或通過包裝一個已有的數組來創建:
ByteBuffer buffer = ByteBuffer.wrap(byteArray);?
?
NIO的強大功能部分來自于channel的非阻塞特性。回顧前面介紹的內容可以知道,套接字的某些操作可能會無限期地阻塞。例如,對accept()方法的調用可能會因為等待一個客戶端連接而阻塞;對read()方法的調用可能會因為沒有數據可讀而阻塞,直到連接的另一端傳來新的數據。總的來說,創建/接收連接或讀寫數據等I/O調用,都可能無限期地阻塞等待,直到底層的網絡實現發生了什么。慢速的、有損耗的網絡,或僅僅是簡單的網絡故障都可能導致任意時間的延遲。然而不幸的是,在調用一個方法之前無法知道其是否會阻塞。NIO的channel抽象的一個重要特征就是可以通過配置它的阻塞行為,以實現非阻塞式的信道。
clntChan.configureBlocking(false);
?
在非阻塞式信道上調用一個方法總是會立即返回。這種調用的返回值指示了所請求的操作完成的程度。例如,在一個非阻塞式ServerSocketChannel上調用accept()方法,如果有連接請求在等待,則返回客戶端SocketChannel,否則返回null。下面我們來創建一個非阻塞式TCP回顯客戶端。可能阻塞的I/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.獲取并轉換參數:第9-16行
2.?創建非阻塞式SocketChannel:第19-20行
3.連接服務器:第23-27行?
由于該套接字是非阻塞式的,因此對connect()方法的調用可能會在連接建立之前返回,如果在返回前已經成功建立了連接,則返回true,否則返回false。對于后一種情況,任何試圖發送或接收數據的操作都將拋出NotYetConnectedException異常,因此,我們通過持續調用finishConnect()方法來"輪詢"連接狀態,該方法在連接成功建立之前一直返回false。打印操作演示了在等待連接建立的過程中,程序還可以執行其他任務。不過,這種忙等的方法非常浪費系統資源,這里這樣做只是為了演示該方法的使用。?
4.創建讀寫緩沖區:第28-29行
?我們分別使用了兩種方法來創建將要用來讀寫數據的ByteBuffer實例。一是通過包裝包含了要發送數據的byte[]數組,另一個方法是調用allocate()方法,創建具有與前面byte[]數組大小相同緩沖區的ByteBuffer實例。
?5.反復循環直到發送和接收完所有字節:第32-41行
只要輸出緩沖區中還留有數據,就調用write()方法。對read()方法的調用不會阻塞等待,但是當沒有數據可讀時該方法將返回0。這里,打印語句再次舉例說明了在等待通信完成的過程中,程序可以執行其他任務。
6.打印接收到的數據:第43-44行
7.關閉信道:第45行
與套接字類似,信道在完成其任務后也需要關閉。
?
相關下載:
Java_TCPIP_Socket編程(doc)
http://download.csdn.net/detail/undoner/4940239
?
文獻來源:
UNDONER(小杰博客) :http://blog.csdn.net/undoner
LSOFT.CN(瑯軟中國) :http://www.lsoft.cn
?
轉載于:https://www.cnblogs.com/wuyida/archive/2012/12/29/6301059.html
總結
以上是生活随笔為你收集整理的NIO:与 Buffer 一起使用 Channel的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 生成短GUID
- 下一篇: [leetcode]Divide Two