非阻塞套接字(Nonblocking sockets)
由於JSR
非阻塞套接字允許在通道上做輸入/輸出操作而不用阻塞使用該通道的進程
Java開發者也許會問
●實現多線程服務為每個連接用戶處理線程
●使用外部第三方模塊
這兩種方法都可以實現
Buffer
在我們考慮非阻塞套接字以前
Buffer類是一個抽象類
●ByteBuffer
●CharBuffer
●DoubleBuffer
●FloatBuffer
●IntBuffer
●LongBuffer
●ShortBuffer
在非阻塞化套接字編程中
你可以使用以下靜態方法(即類方法)來創建一個新的Buffer實例
ByteBuffer buffer
ByteBuffer buffer
ByteBuffer buffer
這段代碼的前兩行創建了兩個ByteBuffer對象
Buffer對象的作用或多或少的與流的作用相似
你可以用put方法寫入一些數據到Buffer中
// Writing on a buffer
IntBuffer buffer = IntBuffer
for (int i=
buffer.put(i);
}
這段代碼創建了一個包含10個整型值的Buffer,然後將數字0到9放入到Buffer中。tW.wiNGWit.cOM同時你可以看到,我使用了capacity方法來獲得Buffer的容量。
要想讀取Buffer的內容,你可以用如下方法來處理:
// Reading from a buffer
buffer.position(0);
while (buffer.hasRemaining()) {
int i = buffer.get();
System.out.println("i="+i);
}
調用position方法,你能設置當前位置為0;即Bufferr的起始位置。當在當前位置和limit值之間有元素時,hasRemaining方法返回true;直到超出這個范圍,此方法將返回flase。while循環中的代碼調用get方法讀取各項,並同時顯示在控制台上。
理解Buffer的limit和capacity這兩個值之間的區別是十分重要的。Capacity是某個Buffer對象所能包含的項數的最大值。Limit是在0到capacity之間的一個值,它表示一個限度,可以使用limit或者flip方法來設置它。我們來看下面的例子:
// Sample of using flip
buffer.position(5);
buffer.flip();
while (buffer.hasRemaining()) {
int i = buffer.get();
System.out.println("i="+i);
}
當前位置被position方法設置成5。Flip方法進行如下操作:先將設置limit為當前位置的值,即5;然後再設置當前位置的值為0。因此,此後的while循環就只能掃描到前5個元素了,因為flip方法設置了新的limit值,即為5。從而,數字0,1,2,3,4將被顯示出來。
另一個重要的Buffer類的方法是clear,它將設置position為0並設置limit為Buffer的容量值。基本上,clear方法消除這之前flip(或limit)方法產生的影響。考慮下例:
// Sample of using clear
buffer.clear();
while (buffer.hasRemaining()) {
int i = buffer.get();
System.out.println("i="+i);
}
這段代碼將顯示數字0到9,而與Buffer的當前位置和limit值無關。
非阻塞(Nonblocking)體系結構
在這一部分,我將從理論的角度來解釋非阻塞體系的結構及其工作原理。這部“喜劇”(當然,如果你喜歡的話也可以稱做戲劇)的“人物”如下:
●服務器端:接收請求的應用程序。
●客戶端:向服務器端發出請求的應用程序。
●套接字通道:客戶端與服務器端之間的通信通道。它能識別服務器端的IP地址和端口號。數據以Buffer中元素的形式通過套接字通道傳送。
●選擇器:所有非阻塞技術的主要對象。它監視著已注冊的套接字通道,並序列化服務器需要應答的請求。
●關鍵字:選擇器用來對對象的請求進行排序。每個關鍵字代表一個單獨的客戶端子請求並包含識別客戶端和請求類型的信息。
圖一:使用非阻塞套接字體系的結構圖。
![](http://tw.wingwit.com/Article/UploadFiles/201311/2013112319091402.gif)
你可能注意到,客戶端應用程序同時執行對服務器端的請求,接著選擇器將其集中起來,創建關鍵字,然後將其發送至服務器端。這看起來像是阻塞(Blocking)體系,因為在一定時間內只處理一個請求,但事實並非如此。實際上,每個關鍵字不代表從客戶端發至服務器端的整個信息流,僅僅只是一部分。我們不要忘了選擇器能分割那些被關鍵字標識的子請求裡的數據。因此,如果有更多連續地數據發送至服務器端,那麼選擇器就會創建更多的根據時間共享策略(Time-sharing policy)來進行處理的關鍵字。強調一下,在圖一中關鍵字的顏色與客戶端的顏色相對應。
服務器端非阻塞(Server Nonblocking)
我以前的部分介紹過的實體都有與其相當的Java實體。客戶端和服務器端是兩個Java應用程序。套接字通道是SocketChannel類的實例,這個類允許通過網絡傳送數據。它們能被Java程序員看作是一個新的套接字。SocketChannel類被定義在java.nio.channel包中。
選擇器是一個Selector類的對象。該類的每個實例均能監視更多的套接字通道,進而建立更多的連接。當一些有意義的事發生在通道上(如客戶端試圖連接服務器端或進行讀/寫操作),選擇器便會通知應用程序處理請求。選擇器會創建一個關鍵字,這個關鍵字是SelectionKey類的一個實例。每個關鍵字都保存著應用程序的標識及請求的類型。其中,請求的類型可以是如下之一:
●嘗試連接(客戶端)
●嘗試連接(服務器端)
●讀取操作
●寫入操作
一個通用的實現非阻塞服務器的算法如下:
create SocketChannel;
create Selector
associate the SocketChannel to the Selector
for(;;) {
waiting events from the Selector;
event arrived; create keys;
for each key created by Selector {
check the type of request;
isAcceptable:
get the client SocketChannel;
associate that SocketChannel to the Selector;
record it for read/write operations
continue;
isReadable:
get the client SocketChannel;
read from the socket;
continue;
isWriteable:
get the client SocketChannel;
write on the socket;
continue;
}
}
基本上,服務器端的實現是由選擇器等待事件和創建關鍵字的無限循環組成的。根據關鍵字的類型,及時的執行操作。關鍵字存在以下4種可能的類型。
Acceptable: 相應的客戶端要求連接。
Connectable:服務器端接受連接。
Readable:服務器端可讀。
Writeable:服務器端可寫。
通常一個表示接受的關鍵
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26359.html