NIO學習二、NIO的基本使用
這是作為學習NIO的總結,如有不對,請大佬指出。
一、基本操作(這些操作不會的時候查文件就行)
從一個buffer中讀寫到另一個buffer
@Test
public void bufferTest1(){
try {
RandomAccessFile readAccessFile=new RandomAccessFile("C:\\Users\\e550c\\Desktop\\日結.txt","rw");
RandomAccessFile writeAccessFile=new RandomAccessFile("C:\\Users\\e550c\\Desktop\\temp.txt","rw");
FileChannel readChannel=readAccessFile.getChannel();
FileChannel writeChannel=writeAccessFile.getChannel();
//將一個channel資料送入另一個channel(這樣做可以實現檔案的寫入)
//與之相對的是transferFrom
readChannel.transferTo(0,readAccessFile.length(), writeChannel);
readAccessFile.close();
writeAccessFile.close();;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
}
其他常見的:
Buffer.rewind()將position設回0,所以你可以重讀Buffer中的所有資料。
mark()與reset()方法 通過呼叫Buffer.mark()方法,可以標記Buffer中的
一個特定position。之後可以通過呼叫Buffer.reset()方法恢復到這個position。
二、Scatter與Gather
scatter:是將channel中的資料寫入到多個buffer中,先寫滿第一個,然後寫入下一個:
Gater:將多個buffer資料先聚集在一起,然後寫入到指定的buffer中。
try {
ByteBuffer byteBuffer1=ByteBuffer.allocate(10);
ByteBuffer byteBuffer2=ByteBuffer.allocate(10);
RandomAccessFile randomAccessFile=new RandomAccessFile("C:\\Users\\e550c\\Desktop\\temp.txt","rw");
byteBuffer1.clear();
byteBuffer2.clear();
byteBuffer1.put("hello".getBytes());
byteBuffer2.put("OK,world".getBytes());
byteBuffer1.flip();
byteBuffer2.flip();
FileChannel fileChannel=randomAccessFile.getChannel();
//將多個byteBuffer聚集在一起寫入
fileChannel.write(new ByteBuffer[]{byteBuffer1,byteBuffer2});
byteBuffer1.clear();
byteBuffer2.clear();
fileChannel.position(0L);
//先寫滿第一個,第一個滿後再寫入第二個
fileChannel.read(new ByteBuffer[]{byteBuffer1,byteBuffer2});
byteBuffer1.flip();
while(byteBuffer1.hasRemaining()){
System.out.print((char)byteBuffer1.get());
}
System.out.println();
byteBuffer2.flip();
while(byteBuffer2.hasRemaining()){
System.out.print((char)byteBuffer2.get());
}
fileChannel.close();
randomAccessFile.close();
}catch (Exception e){
e.printStackTrace();;
}
三、非阻塞IO操作
所有的非阻塞IO的channel都實現了SelectableChannel介面,而FileChannle沒有實現這個介面,所以它不支援非阻塞IO
以一個ServerSocketChannel與SocketChannel為例子來演示:不懂的話可以先看看java網路程式設計。
服務端程式碼:
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 1234));//監聽1234埠
serverSocketChannel.configureBlocking(false);//這樣配置後就是同步非阻塞的
SocketChannel socketChannel = null;
while ((socketChannel = serverSocketChannel.accept()) == null) {//如果沒有客戶端連線的話立即返回
System.out.println("try to Linking:"); //不會阻塞
TimeUnit.SECONDS.sleep(1);
}
ByteBuffer byteBuffer1 = ByteBuffer.allocate(10);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(10);
socketChannel.read(new ByteBuffer[]{byteBuffer1, byteBuffer2});
byteBuffer1.flip();//回到初始位置
byteBuffer2.flip();
while (byteBuffer1.hasRemaining()) {
System.out.println((char) byteBuffer1.get());
}
System.out.println("-----------------");
while (byteBuffer2.hasRemaining()) {
System.out.println((char) byteBuffer2.get());
}
socketChannel.close();//讀取完之後手動關閉這個連線
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
客戶端程式碼
SocketChannel socketChannel=null;
try {
socketChannel=SocketChannel.open();//開啟socket
socketChannel.connect(new InetSocketAddress("127.0.0.1",1234));//連線上指定服務埠
ByteBuffer byteBuffer1=ByteBuffer.allocate(12);
ByteBuffer byteBuffer2=ByteBuffer.allocate(12);
byteBuffer1.put("hello,world".getBytes());
byteBuffer1.flip();
byteBuffer2.put("hello,next".getBytes());
byteBuffer2.flip();
socketChannel.write(byteBuffer1);
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
四、Piple管道操作:
Java NIO管道是兩個執行緒之間單項的資料連線。Piple有一個source通道和一個sink通道。資料會被寫到sink通道,從source通道中讀取。
/*
* Piple進行管道
* */
@Test
public void test16(){
try {
//建立一個piple通道
Pipe pipe=Pipe.open();
//建立一個寫通道
WritableByteChannel writableByteChannel=pipe.sink();
//c建立一個讀通道,從讀通道中獲取資料
ReadableByteChannel readableByteChannel=pipe.source();
//建立一個執行緒從sink寫入資料
WorkerThread workerThread=new WorkerThread(writableByteChannel);
workerThread.start();
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
while(readableByteChannel.read(byteBuffer)>=0){
byteBuffer.flip();
byte[] bytes=new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
String str=new String(bytes);
System.out.println(str);
byteBuffer.clear();
}
readableByteChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static class WorkerThread extends Thread{
WritableByteChannel writableByteChannel;
public WorkerThread(WritableByteChannel writableByteChannel){
this.writableByteChannel=writableByteChannel;
}
@Override
public void run() {
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
for (int i=0;i<10;i++){
String str="piple sink data"+i;
byteBuffer.put(str.getBytes());
byteBuffer.flip();
try {
writableByteChannel.write(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
}
byteBuffer.clear();
}
try{
writableByteChannel.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
五、Selector操作(最重要的,壓軸的&&&)
channle與selector適配使用的,建立了非阻塞的channel之後,註冊到selector中,返
回註冊的SelectionKey,並設定感興趣的事件,也就是說當這些事件發生的時候,對應的
Channel就準備就緒,可以使用。如果沒有發生,那麼就等待。
ps:有時候感覺channel出現很突兀,是因為selector 直接管理 Java Socket 很難實現
,所以使用channel做一次封裝與之適配。這一切都是為了selector而存在的。
如下所示:
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
感興趣的事件是通過SelectionKey來獲得:有如下四種:
SelectionKey.OP_ACCEPT —— 接收連線繼續事件,表示伺服器監聽到了客戶連線,
伺服器可以接收這個連線了
SelectionKey.OP_CONNECT —— 連線就緒事件,表示客戶與伺服器的連線已經建立成功
SelectionKey.OP_READ--讀就緒事件,表示可以讀了(通道目前有資料,可以進行讀操作了)
SelectionKey.OP_WRITE —— 寫就緒事件,表示已經可以向通道寫資料了(通道目前可
以用於寫操作)
操作了(通道目前有資料,可以進行讀操作了)
selector中兩個重要的函式:
selector.select():返回當前就緒的channel數量,如果沒有就緒channel,那麼它就
阻塞,直到就緒channel將它喚醒。
其他的select():
select(long timeout)和select()一樣,除了最長會阻塞timeout毫秒(引數)。
selectNow()不會阻塞,不管什麼通道就緒都立刻返回(此方法執行非阻塞的選擇操作。如果自從前一次選擇操作後,沒有通道變成可選擇的,
則此方法直接返回零)。
selector.SelectionKey():返回已經準備就緒的channel註冊標記的set集合,單個
SelectionKey通過channel()為此對應的SelectionKey建立通道.其他操作查文件就好了。
呼呼~~~~接下來是一個例子:仍然以ServerSocketChannel與SocketChannel為例子:
//服務端:
try {
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",1234));
//設定非阻塞狀態
serverSocketChannel.configureBlocking(false);
//建立一個Selector
Selector selector=Selector.open();
//將serverSocketChannel註冊到selecor中,它的返回值為對應的SelectionKey
//它對ACCPET事件感興趣,也就是說當客戶端連線的時候,它準備就緒,可以被Selector呼叫
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
//分配空間
ByteBuffer byteBuffer=ByteBuffer.allocate(128);
while(true){
//如果沒有就緒事件,它就阻塞
int n=selector.select();
//獲取就緒的channel對應的註冊標記集合
Set<SelectionKey> set=selector.selectedKeys();
Iterator<SelectionKey> iterator=set.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey=iterator.next();
//注意當得到一個SelectionKey之後必須移除它,不然會陷入死迴圈
iterator.remove();
if (selectionKey.isAcceptable()){
//觸發accpet之後,它會將連線的SocketChannel註冊到register中
//它感興趣的事件是讀事件,當通道中有資料之後,它準備就緒
//SocketChannel必須是同步非阻塞的
SocketChannel socketChannel=serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (selectionKey.isReadable()){
SocketChannel socketChannel= (SocketChannel) selectionKey.channel();
byteBuffer.clear();
socketChannel.read(byteBuffer);
byteBuffer.flip();
while(byteBuffer.hasRemaining()){
System.out.print((char)byteBuffer.get());
}
System.out.println();
socketChannel.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
//客戶端
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 1234));
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("hello,world".getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer);
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
另外:針對UDP的資料傳輸,檔案鎖等等等,不是全部能寫完的,多看文件,多練~~
相關文章
- NIO學習一、NIO簡介
- Java NIO學習系列二:ChannelJava
- Java NIO 學習Java
- Nio再學習之NIO的buffer緩衝區
- NIO學習三、基於NIO的WEB伺服器Web伺服器
- java BIO、NIO學習Java
- Java NIO學習系列四:NIO和IO對比Java
- java BIO/NIO/AIO 學習JavaAI
- Java NIO學習系列一:BufferJava
- nio再學習之通道channel
- [網路]NIO學習筆記筆記
- io的基本原理-nio
- Java 多執行緒NIO學習Java執行緒
- Java NIO學習系列三:SelectorJava
- Java NIO 核心元件學習筆記Java元件筆記
- 【NIO】Java NIO之通道Java
- Java NIO Channel 使用Java
- Java NIO學習系列七:Path、Files、AsynchronousFileChannelJava
- Java NIO學習系列五:I/O模型Java模型
- Java NIO 緩衝區學習筆記Java筆記
- Java NIO之Buffer的使用Java
- Java NIO Selector 的使用Java
- 【NIO】Java NIO之緩衝Java
- Java NIO系列2:NIO概述Java
- NIO(二)淺析IO模型模型
- Java NIO系列教程(二) ChannelJava
- Weblogic使用NIO模型Web模型
- 【NIO】Java NIO之選擇器Java
- 深入學習Netty(一)NIO基礎篇Netty
- Java IO學習筆記五:BIO到NIOJava筆記
- NIO相關基礎篇二
- 【死磕NIO】— NIO基礎詳解
- Java NIOJava
- NIO模型模型
- JavaEE進階知識學習----Java NIO-4Java
- 網路程式設計NIO:BIO和NIO程式設計
- JAVA NIO程式設計入門(二)Java程式設計
- 使用Java NIO 和 NIO2實現檔案輸入/輸出Java