Java NIO — 通道(Channel)
阿新 • • 發佈:2018-12-16
NIO 中主要的三個概念為緩衝區、通道、選擇器,它們之間的關係如下所示:
此處要提醒的是,JDK 1.7 升級了 NIO 類庫,升級後的 NIO 類庫被稱為 NIO2.0。在 NIO2.0 中,提供了非同步檔案I/O操作,同時提供了與 UNIX 網路程式設計事件驅動I/O對應的 AIO。
在之前《Java NIO 緩衝區》一文中已經介紹過緩衝區的相關知識,本文主要介紹通道的使用。
通道(Channel)
Channel 用於緩衝區與檔案或套接字之間有效的傳輸資料。傳統的字元/位元組流不同的讀寫操作是分開的,對應於 InputStream 與 OutputStream 兩個類,而 Channel 同時支援讀寫操作,可以更好的對映底層作業系統的API。
Channel 在 Java 中是一個介面,裡面只有兩個方法,用來檢查通道是否開啟與關閉通道:
public interface Channel extends Closeable {
public boolean isOpen();
public void close() throws IOException;
}
Channel介面與其主要實現類的結構圖如下所示:
- ReadableByteChannel:支援讀取位元組的通道
- WritableByteChannel:支援寫入位元組的通道
- NetworkChannel:NIO 2.0 新增,用於加強對 Socket 的操作
- SelectableChannel:支援通過 Selector 實現多路複用的通道
可以看到,FileChannel、SocketChannel、DatagramChannel 都實現了 ReadableByteChannel 與 WritableByteChannel 介面,因此它們都同時支援讀寫操作。
此外,對於圖中的四種Channel來說,它們都是執行緒安全的。
- FileChannel:檔案通道,可以通過在 RandomAccessFile、FileInputStream、FileOutputStream 物件上呼叫 getChannel() 方法獲取,在NIO 2.0 中可以通過 open() 方法直接開啟檔案建立檔案通道
- SocketChannel:通過靜態 open() 方法建立。對應 Socket,通過 socket() 方法獲得與之關聯的 socket 物件
- ServerSocketChannel:通過靜態 open() 方法建立。對應 ServerSocket,通過 accept() 方法接收請求,並返回 SocketChannel 物件
- DatagramChannel:通過靜態 open() 方法建立。對應 DatagramSocket,用於 UDP 資料包的傳輸
通道可以設定為阻塞或非阻塞模式,預設是阻塞模式,可以通過 configureBlocking(false) 方法設定為非阻塞模式。
要注意的是,FileChannel 只能執行在阻塞模式下,其它通道可以在阻塞和非阻塞模式之間選擇。通道需要和位元組緩衝區配合使用。
ServerSocketChannel(伺服器端)示例:
public class SSCDemo {
public static void run() throws IOException, InterruptedException {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(1212));
while (true) {
System.out.println("正在等待連線...");
SocketChannel sc = ssc.accept();
if (sc == null)
TimeUnit.SECONDS.sleep(2);
else {
System.out.println("新連線接入:" + sc.getRemoteAddress());
sc.read(byteBuffer);
byteBuffer.flip();
System.out.print("接收資料:");
while (byteBuffer.hasRemaining()) {
System.out.print((char) byteBuffer.get());
}
System.out.println();
byteBuffer.clear();
}
}
}
public static void main(String[] args) throws IOException, InterruptedException {
SSCDemo.run();
}
}
SocketChannel(客戶端)示例:
public class SCDemo {
public static void run() throws IOException, InterruptedException {
byte[] array = "Hello World!".getBytes();
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.bind(new InetSocketAddress(1213));
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), 1212));
while (!sc.finishConnect()) {
System.out.println("連線中...");
TimeUnit.SECONDS.sleep(2);
}
System.out.println("連線完成!");
sc.write(byteBuffer);
sc.close();
}
public static void main(String[] args) throws IOException, InterruptedException {
SCDemo.run();
}
}
執行結果(伺服器端):
正在等待連線...
正在等待連線...
正在等待連線...
新連線接入:/169.254.77.64:1213
接收資料:Hello World!
正在等待連線...
正在等待連線...