漫談Java IO之 NIO那些事兒

來源:互聯網
上載者:User

標籤:ble   事件   cto   oid   不同的   那些事   引入   har   ini   

前面一篇中已經介紹了基本IO的使用以及最簡單的阻塞伺服器的例子,本篇就來介紹下NIO的相關內容,前面的分享可以參考目錄:

  1. 網路IO的基本知識與概念
  2. 普通IO以及BIO伺服器
  3. NIO的使用與伺服器Hello world
  4. Netty入門與伺服器Hello world
  5. Netty深入淺出

NIO,也叫做new-IO或者non-blocking-IO,就暫且理解為非阻塞IO吧。

為什麼選擇NIO

那麼NIO相對於IO來說,有什麼優勢呢?總結來說:

  1. IO是面向流的,資料只能從一端讀取到另一端,不能隨意讀寫。NIO則是面向緩衝區的,進行資料的操作更方便了
  2. IO是阻塞的,既浪費伺服器的效能,也增加了伺服器的風險;而NIO是非阻塞的。
  3. NIO引入了IO多工器,效率上更高效了。
NIO都有什麼

那麼NIO都提供了什麼呢?

  1. 基於緩衝區的雙向管道,Channel和Buffer
  2. IO多工器Selector
  3. 更為易用的API

Buffer的使用

在NIO中提供了各種不同的Buffer,最常用的就是ByteBuffer:

可以看到,他們都有幾個比較重要的變數:

  • capacity——容量,這個值是一開始申請就確定好的。類似c語言申請數組的大小。
  • limit——剩餘,在寫入模式下初始的時候等於capacity;在讀模式下,等於最後一次寫入的位置
  • mark——標記位,標記一下position的位置,可以調用reset()方法回到這個位置。
  • posistion——位置,寫入模式下表示開始寫入的位置;讀模式下表示開始讀的位置

總結來說,NIO的Buffer有兩種模式,讀模式和寫入模式。剛上來就是寫入模式,使用flip()可以切換到讀模式。

關於這幾個位置的使用,可以參考下面的代碼:

public class ByteBufferTest {    public static void main(String[] args) {        ByteBuffer buffer = ByteBuffer.allocate(88);        System.out.println(buffer);        String value = "Netty權威指南";        buffer.put(value.getBytes());        System.out.println(buffer);        buffer.flip();        System.out.println(buffer);        byte[] v = new byte[buffer.remaining()];        buffer.get(v);        System.out.println(buffer);        System.out.println(new String(v));    }}

得到的輸出為:

java.nio.HeapByteBuffer[pos=0 lim=88 cap=88]java.nio.HeapByteBuffer[pos=17 lim=88 cap=88]java.nio.HeapByteBuffer[pos=0 lim=17 cap=88]java.nio.HeapByteBuffer[pos=17 lim=17 cap=88]Netty權威指南

讀者可以自己領會一下,這幾個變數的含義。另外說明一點,如果遇到自己定義POJO類,就可以像這裡的Buffer重載toString()方法,這樣輸出的時候就很方便了。

最後關於ByteBuffer在Channel中的使用,可以參考下面的代碼:

public class BufferTest {    public static void main(String[] args) throws IOException {        String file = "xxxx/test.txt";        RandomAccessFile accessFile = new RandomAccessFile(file,"rw");        FileChannel fileChannel = accessFile.getChannel();        // 20個位元組        ByteBuffer buffer = ByteBuffer.allocate(20);        int bytesRead = fileChannel.read(buffer);        // buffer.put()也能寫入buffer        while(bytesRead!=-1){            // 寫切換到讀            buffer.flip();            while(buffer.hasRemaining()){                System.out.println((char)buffer.get());            }            // buffer.rewind()重新讀            // buffer.mark()標記position buffer.reset()恢複            // 清除緩衝區            buffer.clear();            // buffer.compact(); 清楚讀過的資料            bytesRead = fileChannel.read(buffer);        }    }}

這樣,就熟悉了Channel和ByteBuffer的使用。接下來,看看伺服器中的應用吧。

NIO伺服器例子

前面BIO的伺服器,是來一個串連就建立一個新的線程響應。這裡基於NIO的多工,可以這樣寫:

import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.Set;public class PlainNioServer {    public void serve(int port) throws IOException {        // 建立channel,並綁定監聽連接埠        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        serverSocketChannel.configureBlocking(false);        ServerSocket ssocket = serverSocketChannel.socket();        InetSocketAddress address = new InetSocketAddress(port);        ssocket.bind(address);        //建立selector,並將channel註冊到selector        Selector selector = Selector.open();        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        final ByteBuffer msg = ByteBuffer.wrap("Hi\r\b".getBytes());        for(;;){            try{                selector.select();            }catch (IOException e){                e.printStackTrace();                break;            }            Set<SelectionKey> readyKeys = selector.selectedKeys();            Iterator<SelectionKey> iterator = readyKeys.iterator();            while(iterator.hasNext()){                SelectionKey key = iterator.next();                iterator.remove();                try{                    if(key.isAcceptable()){                        ServerSocketChannel server = (ServerSocketChannel)key.channel();                        SocketChannel client=  server.accept();                        client.configureBlocking(false);                        client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate());                        System.out.println("accepted connection from "+client);                    }                    if(key.isWritable()){                        SocketChannel client = (SocketChannel) key.channel();                        ByteBuffer buffer = (ByteBuffer) key.attachment();                        while(buffer.hasRemaining()){                           if(client.write(buffer)==0){                               break;                           }                        }                        client.close();                    }                }catch (IOException e){                    key.cancel();                    try{                        key.channel().close();                    } catch (IOException ex){                        ex.printStackTrace();                    }                }            }        }    }    public static void main(String[] args) throws IOException {        PlainNioServer server = new PlainNioServer();        server.serve(5555);    }}

這裡抽象來說是下面的步驟:

  1. 建立ServerSocketChannel並綁定連接埠
  2. 建立Selector多工器,並註冊Channel
  3. 迴圈監聽是否有感興趣的事件發生selector.select();
  4. 獲得事件的控制代碼,並進行處理

其中Selector可以一次監聽多個IO處理,效率就提高很多了。

漫談Java IO之 NIO那些事兒

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.