NIO(四)channel總結
Java NIO Channel
通常來說, 所有的 NIO 的 I/O 操作都是從 Channel 開始的. 一個 channel 類似於一個 stream.
java Stream 和 NIO Channel 對比
-
我們可以在同一個 Channel 中執行讀和寫操作, 然而同一個 Stream 僅僅支援讀或寫.
-
Channel 可以非同步地讀寫, 而 Stream 是阻塞的同步讀寫.
-
Channel 總是從 Buffer 中讀取資料, 或將資料寫入到 Buffer 中.
Channel 型別有:
-
FileChannel, 檔案操作
-
DatagramChannel, UDP 操作
-
SocketChannel, TCP 操作
-
ServerSocketChannel, TCP 操作, 使用在伺服器端.
這些通道涵蓋了 UDP 和 TCP網路 IO以及檔案 IO.
基本的 Channel 使用例子:
public static void main( String[] args ) throws Exception { RandomAccessFile aFile = new RandomAccessFile("/Users/xiongyongshun/settings.xml", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close(); }
FileChannel
FileChannel 是操作檔案的Channel, 我們可以通過 FileChannel 從一個檔案中讀取資料, 也可以將資料寫入到檔案中.注意
, FileChannel 不能設定為非阻塞模式.
開啟 FileChannel
RandomAccessFile aFile = new RandomAccessFile("test.txt", "rw");
FileChannel inChannel = aFile.getChannel();
從 FileChannel 中讀取資料
ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf);
寫入資料
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
關閉
當我們對 FileChannel 的操作完成後, 必須將其關閉
channel.close();
設定 position
long pos channel.position();
channel.position(pos +123);
檔案大小
我們可以通過 channel.size()獲取關聯到這個 Channel 中的檔案的大小. 注意, 這裡返回的是檔案的大小, 而不是 Channel 中剩餘的元素個數.
截斷檔案
channel.truncate(1024);
將檔案的大小截斷為1024位元組.
強制寫入
我們可以強制將快取的未寫入的資料寫入到檔案中:
channel.force(true);
SocketChannel
SocketChannel 是一個客戶端用來進行 TCP 連線的 Channel.
建立一個 SocketChannel 的方法有兩種:
-
開啟一個 SocketChannel, 然後將其連線到某個伺服器中
-
當一個 ServerSocketChannel 接受到連線請求時, 會返回一個 SocketChannel 物件.
開啟 SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
關閉
socketChannel.close();
讀取資料
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
如果 read()返回 -1, 那麼表示連線中斷了.
寫入資料
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
非阻塞模式
我們可以設定 SocketChannel 為非同步模式, 這樣我們的 connect, read, write 都是非同步的了.
連線
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
while(! socketChannel.finishConnect() ){
//wait, or do something else...
}
在非同步模式中, 或許連線還沒有建立, connect 方法就返回了, 因此我們需要檢查當前是否是連線到了主機, 因此通過一個 while 迴圈來判斷.
讀寫
在非同步模式下, 讀寫的方式是一樣的.
在讀取時, 因為是非同步的, 因此我們必須檢查 read 的返回值, 來判斷當前是否讀取到了資料.
ServerSocketChannel
ServerSocketChannel 顧名思義, 是用在伺服器為端的, 可以監聽客戶端的 TCP 連線, 例如:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
while(true){
SocketChannel socketChannel =
serverSocketChannel.accept();
//do something with socketChannel...
}
開啟 關閉
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.close();
監聽連線
我們可以使用ServerSocketChannel.accept()方法來監聽客戶端的 TCP 連線請求, accept()方法會阻塞, 直到有連線到來, 當有連線時, 這個方法會返回一個 SocketChannel 物件:
while(true){
SocketChannel socketChannel =
serverSocketChannel.accept();
//do something with socketChannel...
}
非阻塞模式
在非阻塞模式下, accept()是非阻塞的, 因此如果此時沒有連線到來, 那麼 accept()方法會返回null:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
while(true){
SocketChannel socketChannel =
serverSocketChannel.accept();
if(socketChannel != null){
//do something with socketChannel...
}
}
DatagramChannel
DatagramChannel 是用來處理 UDP 連線的.
開啟
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
讀取資料
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
傳送資料
String newData = "New String to write to file..."
+ System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("example.com", 80));
連線到指定地址
因為 UDP 是非連線的, 因此這個的 connect 並不是向 TCP 一樣真正意義上的連線, 而是它會講 DatagramChannel 鎖住, 因此我們僅僅可以從指定的地址中讀取或寫入資料.
channel.connect(new InetSocketAddress("example.com", 80));
本文連結為: segmentfault.com/a/1190000006824107