• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • BIO、NIO、AIO系列一:NIO

    一、幾個基本概念

    1.同步、異步、阻塞、非阻塞

    同步:用戶觸發IO操作,你發起了請求就得等著對方給你返回結果,你不能走,針對調用方的,你發起了請求你等

    異步:觸發觸發了IO操作,即發起了請求以后可以做自己的事,等處理完以后會給你返回處理完成的標志,針對調用方的,你發起了請求你不等

    阻塞:你調用我,我試圖對文件進行讀寫的時候發現沒有可讀寫的文件,我的程序就會進入等待狀態,等可以讀寫了,我處理完給你返回結果,這里的等待和同步的等待有很大的區別,針對服務提供方的,你調用我我發現服務不可用我等

    非阻塞:你調用我,我試圖對文件讀寫的時候發現沒有讀寫的文件,不等待直接返回,等我發現可以讀寫文件處理完了再給你返回成功標志,針對服務提供方的,你調用我我不等,我處理完了給你返回結果

    2、Java對BIO、NIO、AIO的支持:

    Java BIO : 同步阻塞:你調用我,你等待我給你返回結果,我發現沒有可讀寫的資源我也等待,兩個一起等,JDK1.4以前的唯一選擇,適用于數目比較少并且比較固定的架構,對服務器資源要求比較高,大家都在等資源,等服務提供方處理完了再給你返回結果

    Java NIO :同步非阻塞: 你調用我,你等待我給你返回結果,我發現沒有可以讀寫的資源,我不等待先直接返回,等我發現有可以讀寫的資源以后處理完給你返回結果,適用于連接數目多且連接時間比較短(輕操作)的架構,比如聊天服務器,并發局限于應用中,編程比較復雜,JDK1.4開始支持。

    Java AIO(NIO.2) : 異步非阻塞:你調用我,你不等待繼續做自己的事,我發現沒有可以讀寫的資源,我也不等待繼續做我自己的事,等有可以讀寫的資源的時候我處理完給你返回結果,適用于連接數目多且連接時間比較長(重操作)的架構,比如相冊服務器,充分調用OS參與并發操作,編程比較復雜,JDK7開始支持。

    3、BIO、NIO、AIO適用場景分析:

    BIO方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。

    NIO方式適用于連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,并發局限于應用中,編程比較復雜,JDK1.4開始支持。

    AIO方式使用于連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與并發操作,編程比較復雜,JDK7開始支持。

     

    另外,I/O屬于底層操作,需要操作系統支持,并發也需要操作系統的支持,所以性能方面不同操作系統差異會比較明顯。

    二、NIO基礎

    1、傳統BIO模型-InputStream、OutputStream

    傳統BIO是一種同步的阻塞IO,IO在進行讀寫時,該線程將被阻塞,線程無法進行其它操作。

    IO流在讀取時,會阻塞。直到發生以下情況:1、有數據可以讀取。2、數據讀取完成。3、發生異常。

    服務端:

    BioServer.java

     1 import java.io.IOException;
     2 import java.net.ServerSocket;
     3 import java.net.Socket;
     4 
     5 
     6 public class BioServer {
     7     public static void main(String[] args) {
     8         int port=8080; //服務端默認端口
     9         if(args != null && args.length>0){
    10             try {
    11                 port = Integer.valueOf(args[0]);
    12             } catch (NumberFormatException e) {
    13             }
    14         }
    15         ServerSocket server = null;
    16         try {
    17             server = new ServerSocket(port);
    18             System.out.println("啟動了服務端,端口:"+port);
    19             Socket socket = null;
    20             while(true){
    21                 socket = server.accept();//阻塞等待客戶端連接
    22                 new Thread(new BioServerHandler(socket)).start();
    23             }
    24         } catch (Exception e) {
    25             e.printStackTrace();
    26         } finally {
    27             if(server!=null){
    28                 System.out.println("關閉了服務.");
    29                 try {
    30                     server.close();
    31                     server = null;
    32                 } catch (IOException e) {
    33                     e.printStackTrace();
    34                 }
    35             }
    36         }
    37     }
    38 }

    BioServerHandler.java

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    public class BioServerHandler implements Runnable {
    
        private Socket socket;
        public BioServerHandler(Socket socket){
            this.socket = socket;
        }
        
        @Override
        public void run() {
            BufferedReader in = null;
            try {
                in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
                String body = null;
                while(true){
                    body = in.readLine(); //阻塞等待數據可以被讀取
                    if(body == null){
                        break;
                    }
                    System.out.println("服務器接收到指令:"+body);
                }
            } catch (Exception e) {
                if(in != null){
                    try {
                        in.close();
                        in = null;//
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
                if(socket != null){
                    try {
                        socket.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    this.socket = null;
                }
            }
        }
    
    }

     

    客戶端:

    BioServerClient.java

     1 import java.io.IOException;
     2 import java.io.PrintWriter;
     3 import java.net.Socket;
     4 
     5 public class BioServerClient {
     6 
     7     public static void main(String[] args) {
     8         int port=8080; //服務端默認端口
     9         if(args != null && args.length>0){
    10             try {
    11                 port = Integer.valueOf(args[0]);
    12             } catch (NumberFormatException e) {
    13             }
    14         }
    15         Socket socket = null;
    16         PrintWriter out = null;
    17         try {
    18             socket = new Socket("127.0.0.1", port);
    19             out = new PrintWriter(socket.getOutputStream(), true);
    20             out.println("9527");
    21             System.out.println("客戶端向服務端發送了指令");
    22         } catch (Exception e) {
    23             e.printStackTrace();
    24         } finally {
    25             if(out !=null){
    26                 out.close();
    27                 out = null;
    28             }
    29             if(socket != null){
    30                 try {
    31                     socket.close();
    32                 } catch (IOException e) {
    33                     e.printStackTrace();
    34                 }
    35                 socket = null;
    36             }
    37         }
    38     }
    39 }

    2、偽異步IO模型

    以傳統BIO模型為基礎,通過線程池的方式維護所有的IO線程,實現相對高效的線程開銷及管理。

     服務端:

    TimeServer.java

     1 import java.io.IOException;
     2 import java.net.ServerSocket;
     3 import java.net.Socket;
     4 public class TimeServer {
     5     public static void main(String[] args) {
     6         int port=8080; //服務端默認端口
     7         ServerSocket server = null;
     8         try {
     9             server = new ServerSocket(port);
    10             System.out.println("The time server is start in port:"+port);
    11             Socket socket = null;
    12             //通過線程池的方式維護所有的IO線程,實現相對高效的線程開銷及管理
    13             TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50, 10000);
    14             
    15             while(true){
    16                 socket = server.accept();
    17 //                new Thread(new TimeServerHandler(socket)).start();
    18                 singleExecutor.execute(new TimeServerHandler(socket));
    19             }
    20         } catch (Exception e) {
    21             e.printStackTrace();
    22         } finally {
    23             if(server!=null){
    24                 System.out.println("The time server is close.");
    25                 try {
    26                     server.close();
    27                     server = null;
    28                 } catch (IOException e) {
    29                     e.printStackTrace();
    30                 }
    31             }
    32         }
    33     }
    34 }

    TimeServerHandler.java

     1 import java.io.BufferedReader;
     2 import java.io.IOException;
     3 import java.io.InputStreamReader;
     4 import java.io.PrintWriter;
     5 import java.net.Socket;
     6 import java.util.Date;
     7 
     8 
     9 public class TimeServerHandler implements Runnable {
    10 
    11     private Socket socket;
    12     public TimeServerHandler(Socket socket){
    13         this.socket = socket;
    14     }
    15     
    16     @Override
    17     public void run() {
    18         BufferedReader in = null;
    19         PrintWriter out = null;
    20         try {
    21             in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
    22             out = new PrintWriter(this.socket.getOutputStream(), true);
    23             String currentTime = null;
    24             String body = null;
    25             while(true){
    26                 body = in.readLine();
    27                 if(body == null){
    28                     break;
    29                 }
    30                 System.out.println("The time server(Thread:"+Thread.currentThread()+") receive order:"+body);
    31                 currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
    32                 out.println(currentTime);
    33             }
    34         } catch (Exception e) {
    35             if(in != null){
    36                 try {
    37                     in.close();
    38                     in = null;//
    39                 } catch (IOException e1) {
    40                     e1.printStackTrace();
    41                 }
    42             }
    43             if(out != null){
    44                 try {
    45                     out.close();
    46                     out = null;
    47                 } catch (Exception e1) {
    48                     e1.printStackTrace();
    49                 }
    50             }
    51             if(socket != null){
    52                 try {
    53                     socket.close();
    54                 } catch (IOException e1) {
    55                     e1.printStackTrace();
    56                 }
    57                 this.socket = null;
    58             }
    59         }
    60     }
    61 
    62 }

     服務端線程池TimeServerHandlerExecutePool.java

     1 import java.util.concurrent.ArrayBlockingQueue;
     2 import java.util.concurrent.ExecutorService;
     3 import java.util.concurrent.ThreadPoolExecutor;
     4 import java.util.concurrent.TimeUnit;
     5 
     6 public class TimeServerHandlerExecutePool {
     7 
     8     private ExecutorService executor;
     9     
    10     public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {
    11         executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120l, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
    12     }
    13 
    14     public void execute(Runnable task) {
    15         executor.execute(task);
    16     }
    17 
    18 }
    

     

     客戶端:

    TimeServerClient.java

     1 import java.io.BufferedReader;
     2 import java.io.IOException;
     3 import java.io.InputStreamReader;
     4 import java.io.PrintWriter;
     5 import java.net.Socket;
     6 
     7 public class TimeServerClient {
     8 
     9     public static void main(String[] args) {
    10         int port=8080; //服務端默認端口
    11         Socket socket = null;
    12         BufferedReader in = null;
    13         PrintWriter out = null;
    14         try {
    15             socket = new Socket("127.0.0.1", port);
    16             in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    17             out = new PrintWriter(socket.getOutputStream(), true);
    18             out.println("QUERY TIME ORDER");
    19             System.out.println("Send order to server succeed.");
    20             String resp = in.readLine();
    21             System.out.println("Now is : "+resp);
    22         } catch (Exception e) {
    23             e.printStackTrace();
    24         } finally {
    25             if(out !=null){
    26                 out.close();
    27                 out = null;
    28             }
    29             if(in != null){
    30                 try {
    31                     in.close();
    32                 } catch (IOException e) {
    33                     e.printStackTrace();
    34                 }
    35             }
    36             if(socket != null){
    37                 try {
    38                     socket.close();
    39                 } catch (IOException e) {
    40                     e.printStackTrace();
    41                 }
    42                 socket = null;
    43             }
    44         }
    45     }
    46 }

    3、NIO模型

    NIO(JDK1.4)模型是一種同步非阻塞IO,主要有三大核心部分:Channel(通道),Buffer(緩沖區), Selector(多路復用器)。傳統IO基于字節流和字符流進行操作,而NIO基于Channel和Buffer(緩沖區)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中。Selector(多路復用器)用于監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個線程可以監聽多個數據通道。

    NIO和傳統IO(一下簡稱IO)之間第一個最大的區別是,IO是面向流的,NIO是面向緩沖區的

    IO的各種流是阻塞的。這意味著,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。 NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閑時間用于在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。

    NIO優點:

    1、通過Channel注冊到Selector上的狀態來實現一種客戶端與服務端的通信。

    2、Channel中數據的讀取是通過Buffer , 一種非阻塞的讀取方式。

    3、Selector 多路復用器  單線程模型,  線程的資源開銷相對比較小。

     

    NIO缺點

     

    1. API使用復雜。

     

    2. 需要具備一些多線程編碼能力

     

    3. 斷線重連問題比較嚴重

     

    4. NIO還有一些BUG

     

    Channel(通道)

    傳統IO操作對read()或write()方法的調用,可能會因為沒有數據可讀/可寫而阻塞,直到有數據響應。也就是說讀寫數據的IO調用,可能會無限期的阻塞等待,效率依賴網絡傳輸的速度。最重要的是在調用一個方法前,無法知道是否會被阻塞。

    NIO的Channel抽象了一個重要特征就是可以通過配置它的阻塞行為,來實現非阻塞式的通道。

    Channel是一個雙向通道,與傳統IO操作只允許單向的讀寫不同的是,NIO的Channel允許在一個通道上進行讀和寫的操作。

    FileChannel:文件

    SocketChannel:

    ServerSocketChannel:

    DatagramChannel: UDP

    Buffer(緩沖區)

    Bufer顧名思義,它是一個緩沖區,實際上是一個容器,一個連續數組。Channel提供從文件、網絡讀取數據的渠道,但是讀寫的數據都必須經過Buffer。   

     

    Buffer緩沖區本質上是一塊可以寫入數據,然后可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,并提供了一組方法,用來方便的訪問該模塊內存。為了理解Buffer的工作原理,需要熟悉它的三個屬性:capacity、position和limit。

    屬性:capacity、position和limit

    position和limit的含義取決于Buffer處在讀模式還是寫模式。不管Buffer處在什么模式,capacity的含義總是一樣的。見下圖:

     

    capacity:作為一個內存塊,Buffer有固定的大小值,也叫作“capacity”,只能往其中寫入capacity個byte、long、char等類型。一旦Buffer滿了,需要將其清空(通過讀數據或者清楚數據)才能繼續寫數據。

    position:當你寫數據到Buffer中時,position表示當前的位置。初始的position值為0,當寫入一個字節數據到Buffer中后,position會向前移動到下一個可插入數據的Buffer單元。position最大可為capacity-1。當讀取數據時,也是從某個特定位置讀,將Buffer從寫模式切換到讀模式,position會被重置為0。當從Buffer的position處讀取一個字節數據后,position向前移動到下一個可讀的位置。

    limit:在寫模式下,Buffer的limit表示你最多能往Buffer里寫多少數據。 寫模式下,limit等于Buffer的capacity。當切換Buffer到讀模式時, limit表示你最多能讀到多少數據。因此,當切換Buffer到讀模式時,limit會被設置成寫模式下的position值。換句話說,你能讀到之前寫入的所有數據(limit被設置成已寫數據的數量,這個值在寫模式下就是position)

    Buffer的分配:對Buffer對象的操作必須首先進行分配,Buffer提供一個allocate(int capacity)方法分配一個指定字節大小的對象。

    向Buffer中寫數據:寫數據到Buffer中有兩種方式:

    1、從channel寫到Buffer

    int bytes = channel.read(buf); //將channel中的數據讀取到buf中

    2、通過Buffer的put()方法寫到Buffer

    buf.put(byte); //將數據通過put()方法寫入到buf中

    flip()方法:將Buffer從寫模式切換到讀模式,調用flip()方法會將position設置為0,并將limit設置為之前的position的值。

    從Buffer中讀數據:從Buffer中讀數據有兩種方式:

    1、從Buffer讀取數據到Channel

    int bytes = channel.write(buf); //將buf中的數據讀取到channel中

    2、通過Buffer的get()方法讀取數據

    byte bt = buf.get(); //從buf中讀取一個byte

    rewind()方法:Buffer.rewind()方法將position設置為0,使得可以重讀Buffer中的所有數據,limit保持不變。Buffer中的數據,讀取完成后,依然保存在Buffer中,可以重復讀取

    clear()與compact()方法:一旦讀完Buffer中的數據,需要讓Buffer準備好再次被寫入,可以通過clear()或compact()方法完成。如果調用的是clear()方法,position將被設置為0,limit設置為capacity的值。但是Buffer并未被清空,只是通過這些標記告訴我們可以從哪里開始往Buffer中寫入多少數據。如果Buffer中還有一些未讀的數據,調用clear()方法將被"遺忘 "。compact()方法將所有未讀的數據拷貝到Buffer起始處,然后將position設置到最后一個未讀元素的后面,limit屬性依然設置為capacity。可以使得Buffer中的未讀數據還可以在后續中被使用。

    mark()與reset()方法:通過調用Buffer.mark()方法可以標記一個特定的position,之后可以通過調用Buffer.reset()恢復到這個position上。

    Selector(多路復用器)

    Selector與Channel是相互配合使用的,將Channel注冊在Selector上之后,才可以正確的使用Selector,但此時Channel必須為非阻塞模式。Selector可以監聽Channel的四種狀態(Connect、Accept、Read、Write),當監聽到某一Channel的某個狀態時,才允許對Channel進行相應的操作。

    Connect:某一個客戶端連接成功后

    Accept:準備好進行連接

    Read:可讀

    Write:可寫

    4、NIO示例:

    服務端:

    MultiplexerTimeServer.java

      1 package com.studyio.demo3;
      2 
      3 import java.io.IOException;
      4 import java.net.InetSocketAddress;
      5 import java.nio.ByteBuffer;
      6 import java.nio.channels.SelectionKey;
      7 import java.nio.channels.Selector;
      8 import java.nio.channels.ServerSocketChannel;
      9 import java.nio.channels.SocketChannel;
     10 import java.util.Date;
     11 import java.util.Iterator;
     12 import java.util.Set;
     13 
     14 /**
     15  * 
     16  * @author lgs
     17  * 
     18  * 
     19  */
     20 public class MultiplexerTimeServer implements Runnable {
     21 
     22     private Selector selector;
     23     private ServerSocketChannel serverChannel;
     24     private volatile boolean stop;
     25     
     26     public MultiplexerTimeServer(int port) {
     27         try {
     28             //打開服務端的一個通道channel:ServerSocketChannel
     29             serverChannel = ServerSocketChannel.open();
     30             //把服務端的通道設置為非阻塞模式
     31             serverChannel.configureBlocking(false);
     32             //綁定監聽的端口地址
     33             serverChannel.socket().bind(new InetSocketAddress(port), 1024);
     34             //創建Selector(多路復用器)線程
     35             selector = Selector.open();
     36             //將服務端通道ServerSocketChannel注冊到Selector,交給Selector監聽,告訴客戶端服務端是可以連接的了
     37             serverChannel.register(selector, SelectionKey.OP_ACCEPT);
     38             System.out.println("The time server is start in port:"+port);
     39         } catch (Exception e) {
     40             e.printStackTrace();
     41             System.exit(1);
     42         }
     43     }
     44 
     45     public void stop(){
     46         this.stop = true;
     47     }
     48     @Override
     49     public void run() {
     50         //處理客戶端消息
     51         while(!stop){
     52             try {
     53                 //通過Selector循環準備就緒的Key,這個key指的是客戶端的通道
     54                 selector.select();
     55                 //拿到key以后把key放入迭代器iterator
     56                 Set<SelectionKey> selectionKeys = selector.selectedKeys();
     57                 Iterator<SelectionKey> iterator = selectionKeys.iterator();
     58                 SelectionKey selectionKey = null;
     59                 while(iterator.hasNext()){
     60                     selectionKey = iterator.next();
     61                     //取到key以后就移出,避免重復取
     62                     iterator.remove();
     63                     try {
     64                         //處理客戶端傳遞過來的數據
     65                         handleInput(selectionKey);
     66                     } catch (Exception e) {
     67                         if(selectionKey!=null){
     68                             selectionKey.cancel();
     69                             if(selectionKey.channel()!=null){
     70                                 selectionKey.channel().close();
     71                             }
     72                         }
     73                     }
     74                 }
     75             } catch (Exception e) {
     76                 e.printStackTrace();
     77             }
     78         }
     79         if(selector !=null){
     80             try {
     81                 selector.close();
     82             } catch (IOException e) {
     83                 e.printStackTrace();
     84             }
     85         }
     86     }
     87 
     88     /**
     89      * 處理客戶端傳遞過來的數據
     90      * @param selectionKey
     91      * @throws IOException
     92      */
     93     private void handleInput(SelectionKey selectionKey) throws IOException {
     94         if(selectionKey.isValid()){
     95             //客戶端是可連接的
     96             if (selectionKey.isAcceptable()) {
     97                 ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
     98                 //多路復用器監聽到新的客戶端連接,處理連接請求,完成TCP三次握手。
     99                 SocketChannel client = server.accept();
    100                 //設置為非阻塞模式
    101                 client.configureBlocking(false);
    102                 // 將新連接注冊到多路復用器上,監聽其讀操作,讀取客戶端發送的消息。
    103                 client.register(selector, SelectionKey.OP_READ);
    104             }
    105             //客戶端是可讀的
    106             if(selectionKey.isReadable()){
    107                 //獲取取客戶端的通道
    108                 SocketChannel client = (SocketChannel) selectionKey.channel();
    109                 ByteBuffer receivebuffer = ByteBuffer.allocate(1024);
    110                 //讀取客戶端請求消息到緩沖區
    111                 int count = client.read(receivebuffer);   //非阻塞    
    112                 if (count > 0) {
    113                     receivebuffer.flip();
    114                     byte[] bytes = new byte[receivebuffer.remaining()]; //remaining()方法
    115                     //從緩沖區讀取消息到bytes數組里面
    116                     receivebuffer.get(bytes);
    117                     String body = new String(bytes, "UTF-8");
    118                     System.out.println("The time server(Thread:"+Thread.currentThread()+") receive order : "+body);
    119                     //將currentTime響應給客戶端(客戶端Channel)
    120                     String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
    121                     //服務端向客戶端響應數據,通過客戶端的通道傳遞數據
    122                     doWrite(client, currentTime);
    123                 }else if(count < 0){
    124                     selectionKey.channel();
    125                     client.close();
    126                 }else{
    127                     
    128                 }
    129             }
    130         }
    131     }
    132 
    133     /**
    134      * 服務端向客戶端響應數據,通過客戶端的通道傳遞數據
    135      * @param client
    136      * @param currentTime
    137      * @throws IOException
    138      */
    139     private void doWrite(SocketChannel client, String currentTime) throws IOException {
    140         if(currentTime != null && currentTime.trim().length()>0){
    141             ByteBuffer sendbuffer = ByteBuffer.allocate(1024);
    142             sendbuffer.put(currentTime.getBytes());
    143             sendbuffer.flip();
    144             //將客戶端響應消息寫入到客戶端Channel中。
    145             client.write(sendbuffer);
    146             System.out.println("服務器端向客戶端發送數據--:" + currentTime);
    147         }else{
    148             System.out.println("沒有數據");
    149         }
    150     }
    151 
    152 }

    服務端入口程序TimeServer.java

    public class TimeServer {
    
        public static void main(String[] args) {
            int port=8080; //服務端默認端口
            MultiplexerTimeServer timeServer=new MultiplexerTimeServer(port);
            new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();
        }
    }

     

     客戶端:

    TimeClientHandler.java

      1 package com.studyio.demo3;
      2 
      3 import java.io.IOException;
      4 import java.net.InetSocketAddress;
      5 import java.nio.ByteBuffer;
      6 import java.nio.channels.SelectionKey;
      7 import java.nio.channels.Selector;
      8 import java.nio.channels.SocketChannel;
      9 import java.util.Iterator;
     10 import java.util.Set;
     11 
     12 /**
     13  * 
     14  * @author lgs
     15  * 
     16  */
     17 public class TimeClientHandler implements Runnable {
     18     
     19     private String host;
     20     private int port;
     21     private SocketChannel socketChannel;
     22     private Selector selector;
     23     private volatile boolean stop;
     24     
     25     public TimeClientHandler(String host, int port) {
     26         this.host = host;
     27         this.port = port;
     28         try {
     29             //客戶端打開一個通道SocketChannel
     30             socketChannel = SocketChannel.open();
     31             //創建Selector(多路復用器)線程
     32             selector = Selector.open();
     33             //設置為非阻塞模式
     34             socketChannel.configureBlocking(false);
     35         } catch (Exception e) {
     36             e.printStackTrace();
     37             System.exit(1);
     38         }
     39     }
     40 
     41     @Override
     42     public void run() {
     43         try {
     44             //連接服務端并發送數據
     45             doConnect();
     46         } catch (Exception e) {
     47             e.printStackTrace();
     48             System.exit(1);
     49         }
     50         //處理服務端響應的數據,和服務端處理客戶端發送的數據一樣
     51         while(!stop){
     52             //輪訓通道的狀態
     53             try {
     54                 selector.select(1000);
     55                 Set<SelectionKey> selectionKeys = selector.selectedKeys();
     56                 Iterator<SelectionKey> iterator = selectionKeys.iterator();
     57                 SelectionKey selectionKey = null;
     58                 while(iterator.hasNext()){
     59                     selectionKey = iterator.next();
     60                     //取到key以后就移出,避免重復取
     61                     iterator.remove();
     62                     try {
     63                         //處理服務端響應的數據
     64                         handleInput(selectionKey);
     65                     } catch (Exception e) {
     66                         if(selectionKey!=null){
     67                             selectionKey.cancel();
     68                             if(selectionKey.channel()!=null){
     69                                 selectionKey.channel().close();
     70                             }
     71                         }
     72                     }
     73                 }
     74             } catch (Exception e) {
     75                 e.printStackTrace();
     76                 System.exit(1);
     77             }
     78         }
     79         if(selector !=null){
     80             try {
     81                 selector.close();
     82             } catch (IOException e) {
     83                 e.printStackTrace();
     84             }
     85         }
     86     }
     87 
     88     /**
     89      * 處理服務端響應的數據
     90      * @param selectionKey
     91      * @throws Exception
     92      */
     93     private void handleInput(SelectionKey selectionKey) throws Exception {
     94         if(selectionKey.isValid()){
     95             SocketChannel client = (SocketChannel) selectionKey.channel();
     96             if (selectionKey.isConnectable()){
     97                 if(client.finishConnect()){
     98                     client.register(selector, SelectionKey.OP_READ);
     99                     doWrite(client);
    100                 }else{
    101                     System.exit(1);
    102                 }
    103             }
    104             if (selectionKey.isReadable()) {
    105                 ByteBuffer receivebuffer = ByteBuffer.allocate(1024);
    106                 int count = client.read(receivebuffer);
    107                 if (count > 0) {
    108                     receivebuffer.flip();
    109                     byte[] bytes = new byte[receivebuffer.remaining()]; //remaining()方法
    110                     receivebuffer.get(bytes);
    111                     String body = new String(bytes, "UTF-8");
    112                     System.out.println("Now is "+body);
    113                     this.stop = true;
    114                 }else if(count < 0){
    115                     selectionKey.channel();
    116                     client.close();
    117                 }else{
    118                     
    119                 }
    120             }
    121         }
    122     }
    123 
    124     /**
    125      * 連接服務端并發送數據
    126      * @throws Exception
    127      */
    128     private void doConnect() throws Exception {
    129         //連接服務端
    130         boolean connect = socketChannel.connect(new InetSocketAddress(host, port));
    131         //判斷是否連接成功,如果連接成功,則監聽Channel的讀狀態。
    132         if(connect){
    133             //連接成功就把客戶端的通道注冊到多路復用器上,并設置通道狀態為可讀
    134             socketChannel.register(selector, SelectionKey.OP_READ);
    135             //寫數據  寫給服務端
    136             doWrite(socketChannel);
    137         }else{
    138             //如果沒有連接成功,則向多路復用器注冊Connect(可連接)狀態
    139             socketChannel.register(selector, SelectionKey.OP_CONNECT);
    140         }
    141         
    142     }
    143 
    144     /**
    145      * 寫數據  寫給服務端
    146      * @param channel
    147      * @throws IOException
    148      */
    149     private void doWrite(SocketChannel channel) throws IOException {
    150         ByteBuffer sendbuffer = ByteBuffer.allocate(1024);
    151         sendbuffer.put("QUERY TIME ORDER".getBytes());
    152         sendbuffer.flip();
    153         //向Channel中寫入客戶端的請求指令  寫到服務端 寫到通道里面
    154         channel.write(sendbuffer);
    155         if(!sendbuffer.hasRemaining()){
    156             System.out.println("Send order to server succeed.");
    157         }
    158     }
    159 }

     客戶端程序入口:TimeServerClient.java

    public class TimeServerClient {
        
        public static void main(String[] args) {
            int port=8080; //服務端默認端口
            new Thread(new TimeClientHandler("127.0.0.1", port), "NIO-TimeServerClient-001").start();
        }
    }

     

     

    版權聲明:本文為qq_26676207原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_26676207/article/details/81110519

    智能推薦

    BIO/NIO/AIO

    BIO/NIO/AIO 幾個概念 阻塞IO 和非阻塞IO 這兩個概念是程序級別的。主要描述的是程序請求操作系統IO操作后,如果IO資源沒有準備好,那么程序該如何處理的問題:前者等待;后者繼續執行(但是使用線程一直輪詢,直到有 IO資源準備好了)。 同步IO 和 異步IO,這兩個概念是操作系統級別的。主要描述的是操作系統在收到程序請求IO操作后,如果IO資源沒有準備好,該如何響應程序的問題:前者不響...

    BIO & NIO & AIO

    目錄 1 BIO 2 NIO 3 AIO 1 BIO BIO是同步阻塞模型,其核心是一個客戶端連接對應一個處理線程。實現起來簡單但是吞吐量低,而且客戶端不做讀寫操作的話,服務端會被阻塞(可以做多線程處理)。 BIO的示例代碼如下: 2 NIO NIO是同步非阻塞模型,服務器實現模式為一個線程可以處理多個請求連接,客戶端發送的連接請求都會注冊到多路復用器selector上,多路復用器輪詢到連接有IO...

    BIO、NIO、AIO

    IO模型 IO模型就是說用什么樣的通道進行數據的發送和接收,Java共支持3種網絡編程IO模式:BIO,NIO,AIO   BIO(Blocking IO) 同步阻塞模型,一個客戶端連接對應一個處理線程(可以通過線程池緩解,但不治本) 缺點: 1、IO代碼里read操作是阻塞操作,如果連接不做數據讀寫操作會導致線程阻塞,浪費資源 2、如果線程很多,會導致服務器線程太多,壓力太大。 應用場...

    BIO、NIO、AIO

    I/O socket 服務端 客戶端 同步 阻塞IO server.accept() socket.getInputStream() 非阻塞IO I/O復用(同步非阻塞) FD(文件描述符) 常見的IO多路復用方式有select、poll、epoll。 BIO(Blocking IO,同步阻塞IO) 傳統的BIO模型 NIO(New IO / Non-blocking IO)同步非阻塞 NIO2 ...

    BIO、NIO、AIO

    0 同步異步 阻塞非阻塞 同步異步 同步 兩個任務相互依賴,一個任務依賴于另一個任務某種方式執行。 異步 兩個任務完全獨立,一方的執行不需要等待另一方執行 阻塞非阻塞 阻塞 發送一個請求,調用者必須等待結果返回。當前線程被掛起,當條件就緒后繼續 2.非阻塞 發送一個請求,調用者不用等待結果返回,可以做其他事 1 BIO(Blocking IO) 同步阻塞IO模式,數據的讀寫阻塞在一個線程內等待其完...

    猜你喜歡

    BIO, NIO,AIO

    BIO、NIO、AIO概述 1.BIO、NIO與AIO概述 1).BIO:Block(阻塞的) IO——。 【同步、阻塞】 2).NIO:Non-Block(非阻塞的(同步)IO——JDK1.4開始的。 【同步、非阻塞】 3).AIO:Asynchronous(異步-非阻塞)IO——JDK1.7開始 【異步、非阻塞】 2.阻塞...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡