FileChannel
FileChannel是什麼
FileChannel是一個連接到文件的通道,可以通過文件通道讀寫文件。它無法設置爲非阻塞模式,總是運行在阻塞模式下。
打開FileChannel
我們可以通過使用一個InputStream、OutputStream或RandomAccessFile來獲取一個FileChannel實例。例如:
RandomAccessFile aFile = new RandomAccessFile("D:/demo/data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
讀FileChannel
調用FileChannel.read()方法,例如:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
read()方法返回的int值表示了有多少字節被讀到了Buffer中。如果返回-1,表示到了文件末尾。
寫FileChannel
調用FileChannel.write()方法,例如:
String newData = "Some data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
因爲無法保證write()方法一次能向FileChannel寫入多少字節,因此需要重複調用write()方法,直到Buffer中已經沒有尚未寫入通道的字節。
關閉FileChannel
用完FileChannel後必須將其關閉,例如:
channel.close();
FileChannel的位置
調用position()方法可以獲取FileChannel的當前位置,調用position(long pos)方法設置FileChannel的當前位置。例如:
long pos = channel.position();
channel.position(pos + 100);
如果將位置設置在文件結束符之後,然後從通道中讀數據,讀方法將返回-1 ,也就是文件結束標誌。
如果將位置設置在文件結束符之後,然後向通道中寫數據,文件將撐大到當前位置並寫入數據。這可能導致“文件空洞”,磁盤上物理文件中寫入的數據間有空隙。
獲取文件的大小
long fileSize = channel.size();
截取文件
調用FileChannel.truncate()方法,截取文件時,文件中指定長度後面的部分將被刪除。如:
channel.truncate(1024);
強制寫入磁盤
操作系統一般會將數據緩存在內存中,寫入到FileChannel裏的數據不一定會即時寫到磁盤上。調用force()方法可以將通道里尚未寫入磁盤的數據強制寫到磁盤上。
force()方法有一個boolean類型的參數,true表示同時將文件元數據(權限信息等)寫到磁盤上,例如:
channel.force(true);
SocketChannel
SocketChannel是什麼
SocketChannel是一個連接到TCP網絡套接字的通道。
打開 SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
關閉 SocketChannel
socketChannel.close();
讀SocketChannel
同讀FileChannel。
寫SocketChannel
同寫FileChannel。
非阻塞模式下的連接
非阻塞模式下調用connect(),該方法可能在連接建立之前就返回了。爲了確定連接是否建立,可以調用finishConnect()方法。
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
while(!socketChannel.finishConnect() ){
// wait, or do something else
}
非阻塞模式下的讀
非阻塞模式下,read()方法在尚未讀到任何數據時可能就返回了。所以需要關注它的int返回值,它會告訴你讀取了多少字節。
非阻塞模式下的寫
非阻塞模式下,write()方法在尚未寫出任何數據時可能就返回了。所以需要在循環中調用write()。
ServerSocketChannel
ServerSocketChannel是什麼
ServerSocketChannel 是一個可以監聽新進來的TCP連接的通道,就像標準IO中的ServerSocket一樣。
打開 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
關閉 ServerSocketChannel
serverSocketChannel.close();
監聽新進來的連接
用ServerSocketChannel.accept()方法監聽新進來的連接。當 accept()方法返回的時候,它返回一個包含新進來的連接的 SocketChannel。因此,accept()方法會一直阻塞到有新連接到達。
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
// do something
}
如果是非阻塞模式,accept()方法會立刻返回,如果還沒有新進來的連接,返回的將是null。 因此,需要檢查返回的SocketChannel是否是null.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(80));
serverSocketChannel.configureBlocking(false);
while(true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
//do something
}
}
DatagramChannel
DatagramChannel是什麼
DatagramChannel是一個能收發UDP包的通道。因爲UDP是無連接的網絡協議,所以不能像其它通道那樣讀取和寫入。它發送和接收的是數據包。
打開 DatagramChannel
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9000));
接收數據
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
發送數據
String newData = "Some data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("www.xxx.com", 9000));
連接
由於UDP是無連接的,連接到特定地址並不會像TCP通道那樣創建一個真正的連接,而是鎖住DatagramChannel ,讓其只能從特定地址收發數據。
channel.connect(new InetSocketAddress("www.xxx.com", 9000));
當連接後,也可以使用read()和write()方法,就像在用傳統的通道一樣,只是在數據傳送方面沒有任何保證。