1. 程式人生 > >Java NIO簡介及應用

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
    String(dst,0,dst.length));
  • 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();
        }
    }

github部落格地址

程式碼地址

可查閱官方api文件