Java NIO 由以下幾個核心部分組成:
Channels
Buffers
Selectors
雖然Java NIO 中除此之外還有很多類和組件,但在我看來篙程,Channel胁后,Buffer 和 Selector 構(gòu)成了核心的API伐债。其它組件趣兄,如Pipe和FileLock驻民,只不過是與三個核心組件共同使用的工具類翻具。因此,在概述中我將集中在這三個組件上回还。其它組件會在單獨的章節(jié)中講到裆泳。
Channel 和 Buffer
基本上,所有的 IO 在NIO 中都從一個Channel 開始柠硕。Channel 有點象流工禾。 數(shù)據(jù)可以從Channel讀到Buffer中,也可以從Buffer 寫到Channel中蝗柔。這里有個圖示:
Channel和Buffer有好幾種類型闻葵。下面是JAVA NIO中的一些主要Channel的實現(xiàn):
FileChannel ---------文件
DatagramChannel ------------UDP
SocketChannel ---------------TCP
ServerSocketChannel -------------TCP
正如你所看到的,這些通道涵蓋了UDP 和 TCP 網(wǎng)絡IO癣丧,以及文件IO槽畔。
與這些類一起的有一些有趣的接口瘫怜,但為簡單起見仇穗,我盡量在概述中不提到它們妇智。本教程其它章節(jié)與它們相關的地方我會進行解釋硼身。
以下是Java NIO里關鍵的Buffer實現(xiàn):
ByteBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
CharBuffer
這些Buffer覆蓋了你能通過IO發(fā)送的基本數(shù)據(jù)類型:byte, short, int, long, float, double 和 char垃它。
Java NIO 還有個 MappedByteBuffer筷黔,用于表示內(nèi)存映射文件草巡, 我也不打算在概述中說明唯卖。
Selector
Selector允許單線程處理多個 Channel憎夷。如果你的應用打開了多個連接(通道)莽鸿,但每個連接的流量都很低,使用Selector就會很方便。例如祥得,在一個聊天服務器中兔沃。
這是在一個單線程中使用一個Selector處理3個Channel的圖示:
要使用Selector,得向Selector注冊Channel级及,然后調(diào)用它的select()方法乒疏。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法返回饮焦,線程就可以處理這些事件怕吴,事件的例子有如新連接進來,數(shù)據(jù)接收等县踢。
代碼示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
/*標識數(shù)字*/
private int flag = 0;
/*緩沖區(qū)大小*/
private int BLOCK = 4096;
/*接受數(shù)據(jù)緩沖區(qū)*/
private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
/*發(fā)送數(shù)據(jù)緩沖區(qū)*/
private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
private Selector selector;
public NIOServer(int port) throws IOException {
// 打開服務器套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 服務器配置為非阻塞
serverSocketChannel.configureBlocking(false);
// 檢索與此通道關聯(lián)的服務器套接字
ServerSocket serverSocket = serverSocketChannel.socket();
// 進行服務的綁定
serverSocket.bind(new InetSocketAddress(port));
// 通過open()方法找到Selector
selector = Selector.open();
// 注冊到selector转绷,等待連接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server Start----8888:");
}
// 監(jiān)聽
private void listen() throws IOException {
while (true) {
// 選擇一組鍵,并且相應的通道已經(jīng)打開
selector.select();
// 返回此選擇器的已選擇鍵集硼啤。
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
handleKey(selectionKey);
}
}
}
// 處理請求
private void handleKey(SelectionKey selectionKey) throws IOException {
// 接受請求
ServerSocketChannel server = null;
SocketChannel client = null;
String receiveText;
String sendText;
int count=0;
// 測試此鍵的通道是否已準備好接受新的套接字連接议经。
if (selectionKey.isAcceptable()) {
// 返回為之創(chuàng)建此鍵的通道。
server = (ServerSocketChannel) selectionKey.channel();
// 接受到此通道套接字的連接谴返。
// 此方法返回的套接字通道(如果有)將處于阻塞模式煞肾。
client = server.accept();
// 配置為非阻塞
client.configureBlocking(false);
// 注冊到selector,等待連接
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 返回為之創(chuàng)建此鍵的通道嗓袱。
client = (SocketChannel) selectionKey.channel();
//將緩沖區(qū)清空以備下次讀取
receivebuffer.clear();
//讀取服務器發(fā)送來的數(shù)據(jù)到緩沖區(qū)中
count = client.read(receivebuffer);
if (count > 0) {
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("服務器端接受客戶端數(shù)據(jù)--:"+receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
//將緩沖區(qū)清空以備下次寫入
sendbuffer.clear();
// 返回為之創(chuàng)建此鍵的通道籍救。
client = (SocketChannel) selectionKey.channel();
sendText="message from server--" + flag++;
//向緩沖區(qū)中輸入數(shù)據(jù)
sendbuffer.put(sendText.getBytes());
//將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位
sendbuffer.flip();
//輸出到通道
client.write(sendbuffer);
System.out.println("服務器端向客戶端發(fā)送數(shù)據(jù)--:"+sendText);
client.register(selector, SelectionKey.OP_READ);
}
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
int port = 8888;
NIOServer server = new NIOServer(port);
server.listen();
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOClient {
/*標識數(shù)字*/
private static int flag = 0;
/*緩沖區(qū)大小*/
private static int BLOCK = 4096;
/*接受數(shù)據(jù)緩沖區(qū)*/
private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
/*發(fā)送數(shù)據(jù)緩沖區(qū)*/
private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
/*服務器端地址*/
private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
"localhost", 8888);
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// 打開socket通道
SocketChannel socketChannel = SocketChannel.open();
// 設置為非阻塞方式
socketChannel.configureBlocking(false);
// 打開選擇器
Selector selector = Selector.open();
// 注冊連接服務端socket動作
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 連接
socketChannel.connect(SERVER_ADDRESS);
// 分配緩沖區(qū)大小內(nèi)存
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
int count=0;
while (true) {
//選擇一組鍵,其相應的通道已為 I/O 操作準備就緒索抓。
//此方法執(zhí)行處于阻塞模式的選擇操作钧忽。
selector.select();
//返回此選擇器的已選擇鍵集。
selectionKeys = selector.selectedKeys();
//System.out.println(selectionKeys.size());
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.out.println("client connect");
client = (SocketChannel) selectionKey.channel();
// 判斷此通道上是否正在進行連接操作逼肯。
// 完成套接字通道的連接過程。
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("完成連接!");
sendbuffer.clear();
sendbuffer.put("Hello,Server".getBytes());
sendbuffer.flip();
client.write(sendbuffer);
}
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
client = (SocketChannel) selectionKey.channel();
//將緩沖區(qū)清空以備下次讀取
receivebuffer.clear();
//讀取服務器發(fā)送來的數(shù)據(jù)到緩沖區(qū)中
count=client.read(receivebuffer);
if(count>0){
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("客戶端接受服務器端數(shù)據(jù)--:"+receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
sendbuffer.clear();
client = (SocketChannel) selectionKey.channel();
sendText = "message from client--" + (flag++);
sendbuffer.put(sendText.getBytes());
//將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位
sendbuffer.flip();
client.write(sendbuffer);
System.out.println("客戶端向服務器端發(fā)送數(shù)據(jù)--:"+sendText);
client.register(selector, SelectionKey.OP_READ);
}
}
selectionKeys.clear();
}
}
}