91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

傳統IO與NIO的區別

發布時間:2021-06-22 14:51:37 來源:億速云 閱讀:174 作者:chen 欄目:大數據

本篇內容主要講解“傳統IO與NIO的區別”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“傳統IO與NIO的區別”吧!

我們先來看一段傳統IO的代碼

public class OioServer {public static void main(String[] args) throws IOException {//這里可以直接寫成ServerSocket server = new ServerSocket(10101);        ServerSocket server = new ServerSocket();        server.bind(new InetSocketAddress(10101));        System.out.println("服務器啟動");        while (true) {//此處會阻塞            Socket socket = server.accept();            System.out.println("來了一個新客戶端");            handler(socket);        }
    }public static void handler(Socket socket) {try {byte[] bytes = new byte[1024];            InputStream inputStream = socket.getInputStream();            while (true) {int read = inputStream.read(bytes);                if (read != -1) {
                    System.out.println(new String(bytes,0,read));                }else {break;                }
            }
        } catch (IOException e) {
            e.printStackTrace();        }finally {try {
                System.out.println("socket關閉");                socket.close();            } catch (IOException e) {
                e.printStackTrace();            }
        }
    }
}

使用telnet連接

admindeMacBook-Pro:~ admin$ telnet 127.0.0.1 10101
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

我們會看到OioServer的運行情況

服務器啟動
來了一個新客戶端

但是當我們又使用一個telnet連接進來的時候,OioServer的運行情況沒變,說明一個服務端只能接收一個客戶端點連接,原因在于Socket socket = server.accept();發生了堵塞,現在我們將其改寫成多線程

public class OioServerThread {public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(10101);        ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);        System.out.println("服務器啟動");        while (true) {
            Socket socket = server.accept();            System.out.println("來了一個新客戶端");            service.execute(() -> handler(socket));        }
    }public static void handler(Socket socket) {try {byte[] bytes = new byte[1024];            InputStream inputStream = socket.getInputStream();            while (true) {int read = inputStream.read(bytes);                if (read != -1) {
                    System.out.println(new String(bytes,0,read));                }else {break;                }
            }
        } catch (IOException e) {
            e.printStackTrace();        }finally {try {
                System.out.println("socket關閉");                socket.close();            } catch (IOException e) {
                e.printStackTrace();            }
        }
    }
}

運行可知,當我們啟動了多個telnet進行連接的時候,它是可以一起連接進來的

服務器啟動
來了一個新客戶端
來了一個新客戶端

但是這里有一個問題,我們線程池的可用線程是有限的,不可能無限提供線程來接收大量客戶端的連接,遲早它會無響應被堵塞的。

我們現在來看一下NIO,NIO其實是使用傳統IO的特性創建一個channel(通道),通過該通道來注冊事件SelectionKey

SelectionKey有四種事件

  • SelectionKey.OP_ACCEPT —— 接收連接繼續事件,表示服務器監聽到了客戶連接,服務器可以接收這個連接了

  • SelectionKey.OP_CONNECT —— 連接就緒事件,表示客戶與服務器的連接已經建立成功

  • SelectionKey.OP_READ —— 讀就緒事件,表示通道中已經有了可讀的數據,可以執行讀操作了(通道目前有數據,可以進行讀操作了)

  • SelectionKey.OP_WRITE —— 寫就緒事件,表示已經可以向通道寫數據了(通道目前可以用于寫操作)

 這里 注意,下面兩種,SelectionKey.OP_READ ,SelectionKey.OP_WRITE ,

1.當向通道中注冊SelectionKey.OP_READ事件后,如果客戶端有向緩存中write數據,下次輪詢時,則會 isReadable()=true;

2.當向通道中注冊SelectionKey.OP_WRITE事件后,這時你會發現當前輪詢線程中isWritable()一直為ture,如果不設置為其他事件

public class NIOServer {   // 通道管理器   private Selector selector;   /**    * 獲得一個ServerSocket通道,并對該通道做一些初始化的工作    * 
    * @param port    *            綁定的端口號    * @throws IOException    */   public void initServer(int port) throws IOException {      // 獲得一個ServerSocket通道      ServerSocketChannel serverChannel = ServerSocketChannel.open();      // 設置通道為非阻塞      serverChannel.configureBlocking(false);      // 將該通道對應的ServerSocket綁定到port端口      serverChannel.socket().bind(new InetSocketAddress(port));      // 獲得一個通道管理器      this.selector = Selector.open();      // 將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_ACCEPT事件,注冊該事件后,      // 當該事件到達時,selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞。      serverChannel.register(selector, SelectionKey.OP_ACCEPT);   }   /**    * 采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理    * 
    * @throws IOException    */   public void listen() throws IOException {
      System.out.println("服務端啟動成功!");      // 輪詢訪問selector      while (true) {         // 當注冊的事件到達時,方法返回;否則,該方法會一直阻塞         selector.select();         // 獲得selector中選中的項的迭代器,選中的項為注冊的事件         Iterator<?> ite = this.selector.selectedKeys().iterator();         while (ite.hasNext()) {
            SelectionKey key = (SelectionKey) ite.next();            // 刪除已選的key,以防重復處理            ite.remove();            handler(key);         }
      }
   }   /**    * 處理請求    * 
    * @param key    * @throws IOException    */   public void handler(SelectionKey key) throws IOException {      
      // 客戶端請求連接事件      if (key.isAcceptable()) {
         handlerAccept(key);         // 獲得了可讀的事件      } else if (key.isReadable()) {
         handelerRead(key);      }
   }   /**    * 處理連接請求    * 
    * @param key    * @throws IOException    */   public void handlerAccept(SelectionKey key) throws IOException {
      ServerSocketChannel server = (ServerSocketChannel) key.channel();      // 獲得和客戶端連接的通道      SocketChannel channel = server.accept();      // 設置成非阻塞      channel.configureBlocking(false);      // 在這里可以給客戶端發送信息哦      System.out.println("新的客戶端連接");      // 在和客戶端連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限。      channel.register(this.selector, SelectionKey.OP_READ);   }   /**    * 處理讀的事件    * 
    * @param key    * @throws IOException    */   public void handelerRead(SelectionKey key) throws IOException {      // 服務器可讀取消息:得到事件發生的Socket通道      SocketChannel channel = (SocketChannel) key.channel();      // 創建讀取的緩沖區      ByteBuffer buffer = ByteBuffer.allocate(1024);      int read = channel.read(buffer);      if(read > 0){         byte[] data = buffer.array();         String msg = new String(data).trim();         System.out.println("服務端收到信息:" + msg);                  //回寫數據         ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());         channel.write(outBuffer);// 將消息回送給客戶端      }else{
         System.out.println("客戶端關閉");         key.cancel();      }
   }   /**    * 啟動服務端測試    * 
    * @throws IOException    */   public static void main(String[] args) throws IOException {
      NIOServer server = new NIOServer();      server.initServer(10101);      server.listen();   }

}

NIO與傳統IO最大的不同

  1. NIO有通道的概念,傳統IO沒有這個概念,但通道的概念是基于傳統IO的

  2. 傳統IO的字符接受處理是也是實用的Java原生的序列化流的方式,而NIO是使用ByteBuffer的緩沖區機制。

使用telnet測試,NIO是肯定支持多個客戶端同時操作的,但很重要的一點是NIO是單線程的,傳統IO和NIO的邏輯如下

傳統IO

傳統IO與NIO的區別

NIO

傳統IO與NIO的區別

至于NIO如何多線程,可以參考NIO如何多線程操作 ,這其實也是Netty的原理。

分別用兩個telnet連接

admindeMacBook-Pro:IOServer admin$ telnet 127.0.0.1 10101
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
dsfds
好的

admindeMacBook-Pro:~ admin$ telnet 127.0.0.1 10101
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
22222
好的

服務端顯示如下

服務端啟動成功!
新的客戶端連接
服務端收到信息:dsfds
新的客戶端連接
服務端收到信息:22222

當我們退出其中一個的時候

admindeMacBook-Pro:~ admin$ telnet 127.0.0.1 10101
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
22222
好的^]
telnet> quit
Connection closed.

服務端顯示如下

服務端啟動成功!
新的客戶端連接
服務端收到信息:dsfds
新的客戶端連接
服務端收到信息:22222
客戶端關閉

如果我們使用telnet連接進去以后,直接關閉shell,則服務端會拋出異常

服務端啟動成功!
新的客戶端連接
服務端收到信息:
Exception in thread "main" java.io.IOException: Connection reset by peer
    at sun.nio.ch.FileDispatcherImpl.read0(Native Method)
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
    at sun.nio.ch.IOUtil.read(IOUtil.java:197)
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
    at com.guanjian.websocket.io.NIOServer.handelerRead(NIOServer.java:111)
    at com.guanjian.websocket.io.NIOServer.handler(NIOServer.java:77)
    at com.guanjian.websocket.io.NIOServer.listen(NIOServer.java:59)
    at com.guanjian.websocket.io.NIOServer.main(NIOServer.java:134)

說明在讀取Buffer緩沖區的時候,拋出了異常,所以我們應該在讀取的時候捕獲異常,而不是拋出異常

/** * 處理讀的事件 * 
 * @param key * @throws IOException */public void handelerRead(SelectionKey key) {   // 服務器可讀取消息:得到事件發生的Socket通道   SocketChannel channel = (SocketChannel) key.channel();   // 創建讀取的緩沖區   ByteBuffer buffer = ByteBuffer.allocate(1024);   try {      int read = channel.read(buffer);      if(read > 0){               byte[] data = buffer.array();               String msg = new String(data).trim();               System.out.println("服務端收到信息:" + msg);               //回寫數據               ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());               channel.write(outBuffer);// 將消息回送給客戶端           }else{
               System.out.println("客戶端關閉");               key.cancel();           }
   } catch (IOException e) {
      e.printStackTrace();   }
}

我們現在來證明NIO是單線程的,將以上代碼修改一下

/** * 處理讀的事件 * 
 * @param key * @throws IOException */public void handelerRead(SelectionKey key) {   // 服務器可讀取消息:得到事件發生的Socket通道   SocketChannel channel = (SocketChannel) key.channel();   // 創建讀取的緩沖區   ByteBuffer buffer = ByteBuffer.allocate(1024);   try {      int read = channel.read(buffer);      Thread.sleep(60000);      if(read > 0){               byte[] data = buffer.array();               String msg = new String(data).trim();               System.out.println("服務端收到信息:" + msg);               //回寫數據               ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());               channel.write(outBuffer);// 將消息回送給客戶端           }else{
               System.out.println("客戶端關閉");               key.cancel();           }
   } catch (Exception e) {
      e.printStackTrace();   }
}

我們讓他發送消息的時候睡一分鐘。啟動服務端,連接第一個telnet進來,并發幾個字符

此時我們連進第二個telnet,會發現服務端沒反應,需要等到一分鐘之后,第一個telnet才會收到"好的",而服務端才會顯示"新的客戶端連接"。

說明服務端在處理發送字符的時候被阻塞,NIO為單線程。

到此,相信大家對“傳統IO與NIO的區別”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

乾安县| 广平县| 峨眉山市| 航空| 玛纳斯县| 喜德县| 泰和县| 南宫市| 西平县| 成安县| 彭泽县| 三台县| 古交市| 本溪市| 随州市| 钟祥市| 阳朔县| 贺州市| 沅陵县| 湛江市| 武平县| 武义县| 五河县| 镇宁| 蓝田县| 平凉市| 广东省| 临泉县| 沽源县| 广河县| 六安市| 莫力| 广州市| 门头沟区| 府谷县| 孟州市| 五河县| 铜梁县| 侯马市| 上栗县| 邹城市|