Java NIO簡介及應用
java nio 簡介
Java NIO(New IO)是用於Java(來自Java 1.4)的替代IO API,意味著替代標準 Java IO和Java Networking API。 Java NIO提供了與原來IO API不同的工作方式,但是作用和目的是一樣的。 NIO支援面向緩衝區的,基於通道的IO操作。 NIO將以更加高效的方式進行檔案的讀寫操作。
Java NIO與普通IO的主要區別
io | nio |
---|---|
面向流 | 面向緩衝區(buffer,channel) |
堵塞io | 非堵塞io |
- | 選擇器 |
java nio主要的核心元件
- 緩衝區 buffer
- 通道 Channels
- 選擇器 Selectors
java nio緩衝區buffer
簡介
Buffer是資料的容器,在nio中負責資料的存取,java為不同資料型別提供了相對應的緩衝區型別 如:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 、DoubleBuffer 等。
Buffer的基本使用
通過allocate()方法獲取緩衝區,put()方法存入資料到緩衝區,get()方法獲取緩衝區中的資料
String temp = "abcdefg"; //通過allocate()方法獲取指定大小的緩衝區 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //使用put()方法將資料新增到緩衝區 byteBuffer.put(temp.getBytes()); //緩衝區切換讀取資料模式 byteBuffer.flip(); //獲取緩衝區資料 byte[] dst = new byte[byteBuffer.limit()]; //使用get()方法獲取資料到dst中 byteBuffer.get(dst); System.out.println(new
Buffer的核心屬性
- capacity:容量,表示緩衝區中最大儲存資料的容量,一旦宣告不能改變。
- limit:界限,表示緩衝區中可以操作資料的大小。(limit後資料不能進行讀寫)
- position:位置,表示緩衝區中正在操作資料的位置
- mark: 標記,表示記錄當前position的位置,可通過reset()恢復到mark的位置
mark <= position <= limit <= capacity, 屬性的各種狀態的值可檢視TestBuffer.java中的測試程式碼
直接緩衝區與非直接緩衝區
- 非直接緩衝區:通過allocate()分配緩衝區,緩衝區建立在jvm中。
- 直接緩衝區:通過allocateDirect()方法建立緩衝區,緩衝區建立在系統實體記憶體中。
java nio通道channel
簡介
通道(channel):使用者數 據源節點和目標節點的連線。在Java nio中負責緩衝區中的資料傳輸, channel本身不儲存資料,因此需要配合緩衝區進行傳輸,實現java.nio.channels.Channel介面 ,主要實現類有FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel等。
獲取通的方式
- (1)java針對支援通道的類提供getChannel()方法 本地IO有 FileInputStream/FileOutputStream RandomAccessFile, 網路IO有 Socket 、ServerSocket 、DatagramSocket
- (2)在Java1.7中的NIO.2針對各個通道提供了靜態方法open()
- (3)在Java1.7中的NIO.2的Files工具類的newByteChannel()
簡單示例使用通道和緩衝區複製檔案:
//使用非直接緩衝區 FileInputStream fis = new FileInputStream("chao.png"); FileOutputStream fos = new FileOutputStream("chao2.png"); //1.獲取通道 FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel(); //2.分配指定大小緩衝區 ByteBuffer buffer = ByteBuffer.allocate(3072); //3.將通道的資料存入緩衝區 while (inChannel.read(buffer)!=-1){ //緩衝區切換為讀模式 buffer.flip(); //4.將緩衝區資料寫入通道 outChannel.write(buffer); buffer.clear(); } outChannel.close(); inChannel.close(); fis.close(); fos.close();
使用NIO網路程式設計
堵塞式socket
客戶端
try { //建立連線通道socketChannel SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080)); //本地通道,緩衝區,讀取本地檔案到通道 FileChannel inChannel = FileChannel.open(Paths.get("test2"),StandardOpenOption.READ); ByteBuffer buf = ByteBuffer.allocate(1024); //通道資料儲存到緩衝區 while (inChannel.read(buf)!=-1){ buf.flip(); //緩衝區資料傳輸至socket連線通道 socketChannel.write(buf); buf.clear(); } //關閉連線寫入 --> 而不關閉通道 socketChannel.shutdownOutput(); //接收服務端反饋 int len = 0; while ((len =socketChannel.read(buf))!=-1){ buf.flip(); System.out.println(new String(buf.array(),0,len)); buf.clear(); } inChannel.close(); socketChannel.close(); } catch (IOException e) { e.printStackTrace(); }
服務端
try { //1獲取通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2繫結連線 serverSocketChannel.bind(new InetSocketAddress(8080)); //3獲取客戶端連線的通道 SocketChannel socketChannel = serverSocketChannel.accept(); //建立本地通道,緩衝區接收客戶端資料 FileChannel outChannel = FileChannel.open(Paths.get("test3"),StandardOpenOption.CREATE,StandardOpenOption.WRITE); ByteBuffer buf = ByteBuffer.allocate(1024); //4.從socketChannel接收客戶端資料 while (socketChannel.read(buf)!=-1){ buf.flip(); outChannel.write(buf); buf.clear(); } //返回資料給客戶端 buf.put("服務端接收資料成功".getBytes()); buf.flip(); socketChannel.write(buf); socketChannel.close(); serverSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); }
非堵塞式socket
客戶端
//1.獲取通道 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080)); //2.設定為非堵塞模式 socketChannel.configureBlocking(false); ByteBuffer buf = ByteBuffer.allocate(1024); //3.傳送資料給服務端 //控制檯輸入資料 Scanner scanner = new Scanner(System.in); while (scanner.hasNext()){ String msg = scanner.next(); buf.put(msg.getBytes()); buf.flip(); socketChannel.write(buf); buf.clear(); } //4.關閉連線 socketChannel.close();
服務端
//1.獲取通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.切換到非堵塞模式 serverSocketChannel.configureBlocking(false); //3.繫結埠號 serverSocketChannel.bind(new InetSocketAddress(8080)); //4.獲取選擇器 Selector selector = Selector.open(); //5.將通道註冊到選擇器上,並且指定“監聽接收事件” serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT); //6輪詢式的獲取選擇器上已經‘準備就緒’的事件 while (selector.select()>0){ //7 。獲取當前選擇器中所有註冊的"選擇健(已就緒的監聽事件)" Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ //8.獲取“準備就緒”的事件 SelectionKey selectionKey = iterator.next(); //9.判斷具體事件,就緒 if (selectionKey.isAcceptable()){ //10.接收就緒,獲取客戶端連線 SocketChannel socketChannel = serverSocketChannel.accept(); //11,切換到非堵塞模式 socketChannel.configureBlocking(false); //12.將客戶端通道註冊到選擇器上 socketChannel.register(selector,SelectionKey.OP_READ); }else if (selectionKey.isReadable()){ //獲取當前選擇器上“讀就緒”狀態的通道 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); //讀取客戶端傳過來的資料 int len = 0; while ((len = socketChannel.read(buffer))>0){ buffer.flip(); System.out.println(new String(buffer.array(),0,len)); buffer.clear(); } } //取消選擇鍵selectionKey iterator.remove(); } }