三.Charset(字元集)
在java.nio.charset包中共提供了Charset、CharsetDecoder、CharsetEncoder、CodeResult、CodingErrorAction五個類,均繼承自Object類,其中Charset實現了Comparable介面,其它類均為自身實現。
Java中的字元使用unicode編碼,每個字元佔用兩個位元組。位元組碼本身只是一些數字,放在正確的上下文中可以被正確的解析。向ByteBuffer中存放資料時需要考慮字元集的編碼方式,從中讀取時需要考慮字元集的解碼。
要讀和寫文本需要分別使用CharsetDecoder(解碼器)和CharsetEncoder(編碼器)。
編碼:百科中這樣定義,編碼(coding)是在一個主題或單元上為資料存放區,管理和分析的目的而轉換資訊為編碼值(典型地如數字)的過程。在密碼學中,編碼是指在編碼或密碼中寫的行為。n位位元可以組合成2的n次方個不同的資訊,給每個資訊規定一個具體碼組,這種過程也叫編碼。數字系統中常用的編碼有兩類,一類是二進位編碼,另一類是二—十進位編碼。
(1)如何得到一個CharSet?
在JDK源碼中提供兩種方式得到一個CharSet執行個體:
CharSet cs = CharSet.forName(“編碼方式”);
CharSet cs = CharSet.defaultCharSet();
第一種方法返回一個指定字元格式設定的CharSet,第二種方法返回當前虛擬機器預設的字元編碼格式的CharSet。
(2)如何使用CharSet?
得到一個CharSet執行個體後,我們需要建立一個編碼器和一個解碼器,使用下面方法進行建立:
CharSetDecoder decoder = cs.newDecoder();
CharSetEncoder encoder = cs.newEncoder();
接著我們把ByteBuffer傳遞給decoder進行編碼,返回一個CharBuffer:
CharBuffer cb = decoder.decode(inputData);
然後我們可以使用encoder進行解碼返回一個ByteBuffer:
ByteBuffer outputData = encoder.encode(cb);
接下來可以進行寫等其它操作。
四.Selector(非同步IO)
非同步IO是一種沒有阻塞的讀寫資料的方法,通常,在代碼進行 read() 調用時,代碼會阻塞直至有可供讀取的資料。同樣,write() 調用將會阻塞直至資料能夠寫入。
非同步 I/O 的一個優勢在於,它允許您同時根據大量的輸入和輸出執行 I/O。同步程式常常要求助於輪詢,或者建立許許多多的線程以處理大量的串連。使用非同步 I/O,您可以監聽任何數量的通道上的事件,不用輪詢,也不用額外的線程。
非同步 I/O 中的核心對象名為 Selector。Selector 就是您註冊對各種 I/O 事件的地方,而且當那些事件發生時,就是這個對象告訴您所發生的事件。
第一步:建立一個Selector
Selector selector = Selector.open();
第二步:開啟一個遠端連線
InetSocketAddress socketAddress =
new InetSocketAddress("www.baidu.com", 80);
SocketChannel sc = SocketChannel.open(socketAddress);
sc.configureBlocking(false);
第三步:選擇鍵,註冊
SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);
註冊時第一個參數總是當前的這個selector。
註冊讀事件:SelectionKey key = sc.register(selector, SelectionKey.OP_READ);
註冊寫事件:SelectionKey key = sc.register(selector, SelectionKey.OP_WRITE);
第四步:內部迴圈處理
int num = selector.select();
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
// ... deal with I/O event ...
}
首先,我們調用 Selector 的 select() 方法。這個方法會阻塞,直到至少有一個登入的事件發生。當一個或者更多的事件發生時, select() 方法將返回所發生的事件的數量。該方法必須首先執行。
接下來,我們調用 Selector 的 selectedKeys() 方法,它返回傳生了事件的 SelectionKey 對象的一個 集合 。
我們通過迭代 SelectionKeys 並依次處理每個 SelectionKey 來處理事件。對於每一個 SelectionKey,您必須確定發生的是什麼 I/O 事件,以及這個事件影響哪些 I/O 對象。
第五步:監聽事件並做出處理
SelectionKey中共定義了四種事件,OP_ACCEPT(socket accept)、OP_CONNECT(socket connect)、OP_READ(read)、OP_WRITE(write)。
第六步:刪除處理過的SelectionKey
在處理 SelectionKey 之後,我們幾乎可以返回主迴圈了。但是我們必須首先將處理過的 SelectionKey 從選定的鍵集合中刪除。
如果我們沒有刪除處理過的鍵,那麼它仍然會在主集合中以一個啟用的鍵出現,這會導致我們嘗試再次處理它。
我們調用迭代器的 remove() 方法來刪除處理過的 SelectionKey:it.remove();