1、什麼是java nio前端
java nio 是java new i/o的簡稱,也有叫java non-blocking i/o,在jdk1.4中引入。它是一種同步非阻塞的io模型,也是io多路複用的基礎。java
2、nio技術組成緩存
java NIO主要由三部分組成①channels、②selecters、③Buffers 服務器
一、Channel(通道)相似於bio中的stream,數據能夠從channel讀取到buffer中,也能夠從buffer寫入channel中。channel和stream的不一樣點是stream是單向的,二channel是雙向的。下面是一些常見的通道,網絡
FileChannel 從文件中讀寫數據,不能設置爲非阻塞模式,也就不能與selector在一塊兒使用app
DatagramChannel 經過UDP讀寫網絡中的數據dom
SocketChannel 經過TCP讀寫網絡中的數據socket
ServerSocketChannel 能夠監聽新進來的TCP連接,對每個新進來的TCP連接都會建立一個SocketChannelpost
2.Buffers 緩存,用來緩存從channel中讀取的數據,也能夠將數據從緩存寫入channel中。spa
ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer 從命名可知分別對應了幾種基本數據類型。
MappedByteBuffer 是ByteBuffer的子類,用於表示內存映射文件緩存,可用於複製大型文件。
Buffer的屬性和一些經常使用的方法:
屬性:mark <= position <= limit <= capacity
capacity :緩存區的總容量
position :緩衝區中,下一次發生讀取或寫入操做的地方,每次讀寫操做後,都會向後推動。
limit:在讀模式下,limit和positon一相同;在寫模式下,limit設置爲上次讀取時position的地方
mark:標記值,當調用了mark()方法後,mark就是當前的position位置;調用reset()以後,會將postion置爲mark的位置。
方法:
allocate(n) 初始化緩衝區的大小,此時position=limit=0 ,capacity = n, mark= -1
put() 向緩衝區寫數據
get() 向緩衝區讀數據
filp() 將緩衝區從寫模式切換到讀模式
clear() 從讀模式切換到寫模式,不會清空數據,但後續寫數據會覆蓋原來的數據,即便有部分數據沒有讀,也會被遺忘;將position和limit都置爲0,將mark置爲-1
compact() 從讀數據切換到寫模式,數據不會被清空,會將全部未讀的數據copy到緩衝區頭部,後續寫數據不會覆蓋,而是在這些數據以後寫數據
mark() 對position作出標記,配合reset使用
reset() 將position置爲標記值
rewind() 將position置爲0,mark置爲-1
3.selector 選擇器,用於監聽在selector上註冊了的channel的事件,只能用於非阻塞channel。經過調用它的select()或其重載方法,這個方法會一直阻塞,直到註冊的通道中有事件就緒。
在selector上註冊channel時須要指定監聽的事件,總共有以下四種事件可供選擇:
SelectionKey.OP_CONNECT 若是某個channel成功鏈接到另外一個服務器稱爲「鏈接就緒」
SelectionKey.OP_ACCEPT 若是一個ServerSocketChannel準備好接收新進入的鏈接稱爲「接收就緒」
SelectionKey.OP_READ 若是一個有數據可讀的通道能夠說是「讀就緒」
SelectionKey.OP_WRITE 等待寫數據的通道能夠說是「寫就緒」
下面是一些nio方法操做文件的例子:
//NIO讀寫文件 public class Test3 { public static void main(String[] args) { String path = "E:/a.txt"; readFile(path); String path2 = "E:/b.txt"; writeFile(path2); } /** * * @Title: readFile * @Description:使用FileChannel讀取文本文件 * @param @param path * @return void * @throws */ public static void readFile(String path){ FileInputStream fis = null; FileChannel fc = null; try { //Java.nio.charset.Charset處理了字符轉換問題。它經過構造CharsetEncoder和CharsetDecoder將字符序列轉換成字節和逆轉換。 Charset charset = Charset.forName("utf-8"); CharsetDecoder decoder = charset.newDecoder(); fis = new FileInputStream(path); fc = fis.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); CharBuffer charBuffer = CharBuffer.allocate(1024); while (fc.read(byteBuffer) != -1) { byteBuffer.flip(); decoder.decode(byteBuffer, charBuffer, false);//若是不夠一個字符不會進行解碼 charBuffer.flip(); while (charBuffer.hasRemaining()) { System.out.print(charBuffer.get()); } byteBuffer.compact();//將緩衝區設置爲讀模式,而且上一次沒有轉換的字節會被保留下來,並放在最前端,不能用clear()代替 charBuffer.compact(); } fis.close(); fc.close(); } catch (Exception e) { e.printStackTrace(); }finally{ try { fis.close(); fc.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * * @Title: writeFile * @Description:使用FileChannel將文字寫入文件中 * @param @param path * @return void * @throws */ public static void writeFile(String path){ FileOutputStream fos = null; FileChannel fileChannel = null; try { String source = "我要寫入一個文件中使得發放卡號便可adda"; fos = new FileOutputStream(path); fileChannel = fos.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put(source.getBytes("utf-8")); buffer.flip(); fileChannel.write(buffer); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { fos.close(); fileChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
//nio複製文件 public class Test4 { public static void main(String[] args) throws IOException { String source = "E:/BaiduNetdisk_5.6.3.exe"; String target = "E:/BaiduNetdisk_5.6.3_copy.exe"; // bioCopyFile(source,target); // nioCopyFile(source,target); mappedCopyFile(source,target); } /** * * @Title: bioCopyFile * @Description: java 標準io流複製文件 * @param @throws IOException * @return void * @throws */ public static void bioCopyFile(String source,String target)throws IOException { long startTime = System.currentTimeMillis(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target)); int len = 0; while((len = bis.read()) != -1){ bos.write(len); } long endTime = System.currentTimeMillis(); System.out.println("bio複製文件所用時間"+(endTime-startTime)+"毫秒"); bis.close(); bos.close(); } /** * * @Title: nioCopyFile * @Description: java nio複製文件 * @param @throws IOException * @return void * @throws */ public static void nioCopyFile(String source,String target)throws IOException { long startTime = System.currentTimeMillis(); RandomAccessFile raf = new RandomAccessFile(source,"rw"); RandomAccessFile raf2 = new RandomAccessFile(target,"rw"); FileChannel inChannel = raf.getChannel(); FileChannel outChannel = raf2.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while(inChannel.read(buffer) != -1){ buffer.flip();//寫模式切換到讀模式 outChannel.write(buffer); buffer.clear();//讀模式切換到寫模式 } raf.close(); raf2.close(); inChannel.close(); outChannel.close(); long endTime = System.currentTimeMillis(); System.out.println("nio複製文件所用時間"+(endTime-startTime)+"毫秒"); } /** * * @Title: mappedCopyFile * @Description: 使用MappedByteBuffer來複制文件 * MappedByteBuffer的確快,但也存在一些問題,主要就是內存佔用和文件關閉等不肯定問題。 * 被MappedByteBuffer打開的文件只有在垃圾收集時纔會被關閉,而這個點是不肯定的 */ public static void mappedCopyFile(String source,String target) throws IOException{ long startTime = System.currentTimeMillis(); RandomAccessFile raf = new RandomAccessFile(source,"rw"); RandomAccessFile raf2 = new RandomAccessFile(target,"rw"); FileChannel inChannel = raf.getChannel(); FileChannel outChannel = raf2.getChannel(); MappedByteBuffer mappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); outChannel.write(mappedByteBuffer); raf.close(); raf2.close(); inChannel.close(); outChannel.close(); long endTime = System.currentTimeMillis(); System.out.println("MappedByteBuffer複製文件所用時間"+(endTime-startTime)+"毫秒"); } }
//nio服務器 public class Server { private Selector selector;//同一個選擇器,用來監聽是否有連接或者其餘操做就緒 public static void main(String[] args) { Server server = new Server(); server.startServer();//啓動服務器時監聽端口是否有新的鏈接事件 server.listenChannel();//監聽全部的channel有沒有數據讀取準備就緒 } public void startServer(){ try { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ssc.socket().bind(new InetSocketAddress(8888));//監聽8888端口 selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException e) { e.printStackTrace(); } } public void listenChannel(){ while(true){ try { selector.select(); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); handleKey(key); iterator.remove(); } } catch (IOException e) { e.printStackTrace(); } } } private void handleKey( SelectionKey key){ try { if(key.isAcceptable()){//有新的網絡能夠連接 ServerSocketChannel channel = (ServerSocketChannel)key.channel(); SocketChannel socketChannel = channel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } if(key.isReadable()){ SocketChannel channel = (SocketChannel)key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read = channel.read(buffer); if(read > 0){ String receiveText = new String(buffer.array(),0,read); System.out.println("服務器端接受到的數據---"+receiveText); channel.register(selector, SelectionKey.OP_WRITE); } } if(key.isWritable()){ SocketChannel channel = (SocketChannel)key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.clear(); buffer.put("服務器響應信息".getBytes()); buffer.flip(); channel.write(buffer); channel.register(selector, SelectionKey.OP_READ); } } catch (IOException e) { e.printStackTrace(); } } } //nio客戶端 public class Client { public static void main(String[] args) { clientStart(); } public static void clientStart(){ try { Selector selector = Selector.open(); SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress("localhost", 8888)); sc.register(selector, SelectionKey.OP_CONNECT); while(true){ selector.select(); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); //iterator.remove(); if(key.isConnectable()){ System.out.println("client connect"); SocketChannel socketChannel = (SocketChannel) key.channel(); // 判斷此通道上是否正在進行鏈接操做。 // 完成套接字通道的鏈接過程。 ByteBuffer sendBuffer = ByteBuffer.allocate(1024); //完成鏈接的創建(TCP三次握手) if (socketChannel.isConnectionPending()) { socketChannel.finishConnect(); System.out.println("完成鏈接!"); sendBuffer.put("Hello,Server".getBytes()); sendBuffer.flip(); socketChannel.write(sendBuffer); } socketChannel.register(selector, SelectionKey.OP_READ); } if(key.isReadable()){ SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read=channel.read(buffer); if(read>0){ String receiveText = new String( buffer.array(),0,read); System.out.println("客戶端接受服務器端數據--:"+receiveText); channel.register(selector, SelectionKey.OP_WRITE); } } if(key.isWritable()){ SocketChannel socketChannel = (SocketChannel)key.channel(); ByteBuffer sendBuffer = ByteBuffer.allocate(1024); sendBuffer.clear(); sendBuffer.put("MSG from client".getBytes()); sendBuffer.flip(); socketChannel.write(sendBuffer); socketChannel.register(selector, SelectionKey.OP_READ); } } } } catch (IOException e) { e.printStackTrace(); } } }