<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                2021-10-02 周六 ## 緣起 最近在看《Java NIO》這本書,書中詳細講解了`jdk1.4`內提供的關于實現`nio`的`API`。因為閱讀后,發現對于NIO還是學習的不夠深入,之前也僅僅是學習了Java的文件IO和Socket編程,再者也是用`Netty`框架編寫NIO代碼,并未用Java提供的NIO實踐。借此機會,把Java關于網絡IO的發展給整理清楚,并編寫Java代碼示例,加深理解!!! IO其實分為文件IO和流IO,這里討論的是流IO,也就是Socket的IO。 ## 圖解Java網絡IO發展歷程 ![](https://img.kancloud.cn/28/c8/28c8d22836722dc7f3b3bfb517638287_1526x1104.png) * 1996年1月發布`jdk1.0`版本,支持Java BIO的`Socket`編程。 * *2001年1月`Linux`內核發布2.4版本,支持NIO,非阻塞IO。* * 2002年2月發布`jdk1.4`版本,支持Java NIO的`SocketChannel`編程,支持非阻塞。 * 2011年7月發布`jdk1.7`版本,支持Java AIO的`AsynchronousServerSocketChannel`編程。 ## Java實現BIO(阻塞) ![](https://img.kancloud.cn/d1/23/d1230945ed0b386bb97b5cbc5651de3f_1862x1300.png) ### SocketServerDemo 服務端 ``` java package org.mango.demo.bio; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; /** * 1連接1線程模型(線程資源有限,不適用) * 將連進來的連接交給新的線程處理,這樣主線程負責處理客戶端連接,工作線程處理連接數據讀取和寫入 * 支持客戶端多連接 * @Description socket server 編寫的bio服務端 jdk1.0 * @Date 2021-10-02 15:19 * @Created by mango */ public class SocketServerDemo { public static void main(String[] args) throws IOException { // jdk1.0 版本,基于ServerSocket ServerSocket serverSocket = new ServerSocket(); int port = 9000; serverSocket.bind(new InetSocketAddress(port)); System.out.println("server listen on port " + port); while (true) { // 調用accept()方法,會阻塞 Socket socket = serverSocket.accept(); System.out.println(socket + " connect success!!"); // 將連進來的連接交給新的線程處理,這樣主線程負責處理客戶端連接,工作線程處理連接數據讀取和寫入 // 支持客戶端多連接 new WorkThread(socket).start(); } } } /** * 工作線程 */ class WorkThread extends Thread{ private Socket socket; public WorkThread(Socket socket){ this.socket = socket; } @Override public void run() { String tn = Thread.currentThread().getName(); String sn = socket.toString(); // 將接收到的數據后跟 處理線程名稱 發送給客戶端 boolean loop = true; while(loop){ try { DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); // 從inputStream流讀取數據,會阻塞 String data = dis.readUTF(); System.out.println(tn + ":" + sn + " receive data:" + data); String rData = data + ":" +tn; dos.writeUTF(rData); } catch (IOException e) { // 連接異常關閉,線程結束 try { socket.close(); loop = false; System.out.println(tn + ":" + sn + " exception close!"); } catch (IOException e1) { e1.printStackTrace(); } } } } } /** * 執行結果: * server listen on port 9000 * Socket[addr=/127.0.0.1,port=60357,localport=9000] connect success!! * Socket[addr=/127.0.0.1,port=60387,localport=9000] connect success!! * Thread-0:Socket[addr=/127.0.0.1,port=60357,localport=9000] receive data:你好 * Thread-1:Socket[addr=/127.0.0.1,port=60387,localport=9000] receive data:中國 * Thread-0:Socket[addr=/127.0.0.1,port=60357,localport=9000] exception close! * Thread-1:Socket[addr=/127.0.0.1,port=60387,localport=9000] exception close! */ ``` 執行結果打印: ``` java server listen on port 9000 Socket[addr=/127.0.0.1,port=60357,localport=9000] connect success!! Socket[addr=/127.0.0.1,port=60387,localport=9000] connect success!! Thread-0:Socket[addr=/127.0.0.1,port=60357,localport=9000] receive data:你好 Thread-1:Socket[addr=/127.0.0.1,port=60387,localport=9000] receive data:中國 Thread-0:Socket[addr=/127.0.0.1,port=60357,localport=9000] exception close! Thread-1:Socket[addr=/127.0.0.1,port=60387,localport=9000] exception close! ``` ### SocketClientDemo 客戶端 ``` java package org.mango.demo.bio; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Scanner; /** * @Description TODO * @Date 2021-10-02 23:15 * @Created by mango */ public class SocketClientDemo { public static void main(String[] args) throws IOException { Socket socket = new Socket(); // 連接服務端 String host = "127.0.0.1"; int port = 9000; socket.connect(new InetSocketAddress(host,port)); System.out.println("connect server " + host + ":" + port + " success!"); // 接收命令 Scanner scanner = new Scanner(System.in); System.out.println("輸入exit:客戶端退出;其他為發送數據!"); // 發送數據 DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); DataInputStream dis = new DataInputStream(socket.getInputStream()); boolean loop = true; while(loop) { System.out.print("請輸入:"); String input = scanner.next(); if("exit".equals(input)){ socket.close(); loop = false; }else{ dos.writeUTF(input); System.out.println("send success,data:" + input); // 等待服務端發送數據,readUTF 方法會阻塞 String sData = dis.readUTF(); System.out.println("receive data:" + sData); } } System.out.println("client exit!"); } } 執行結果打印: ``` java 執行結果1: connect server 127.0.0.1:9000 success! 輸入exit:客戶端退出;其他為發送數據! 請輸入:你好 send success,data:你好 receive data:你好:Thread-0 請輸入:exit client exit! ``` ``` java 執行結果2: connect server 127.0.0.1:9000 success! 輸入exit:客戶端退出;其他為發送數據! 請輸入:中國 send success,data:中國 receive data:中國:Thread-1 請輸入:exit client exit! ``` ***總結:*** ***1. Socket默認就是阻塞的,并不支持設置非阻塞。*** ***2. accept() 和 從流讀取數據的read() 都是阻塞的。*** ## Java實現NIO ### 單線程版本 `NonBlockServerDemo` - 服務端 ``` java package org.mango.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * @Description (服務端非阻塞)主動遍歷 一個線程處理客服端連接,數據讀取 * @Date 2021-10-01 15:26 * @Created by mango */ public class NonBlockServerDemo { public static void main(String[] args) throws IOException, InterruptedException { // 存放連接進來的SocketChannel List<SocketChannel> scList = new ArrayList<>(); // 服務端,使用ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); int port = 9000; // 綁定本地端口 serverSocketChannel.bind(new InetSocketAddress(port)); System.out.println("server listen on " + port); // 設置服務端通道非阻塞 serverSocketChannel.configureBlocking(false); while (true){ // accept方法非阻塞,立即返回,null表示沒有client連接進來 SocketChannel sc = serverSocketChannel.accept(); if(null == sc){ Thread.sleep(200); }else{ System.out.println(Thread.currentThread().getName() + " client enter " + sc.toString()); // 設置連接非阻塞 sc.configureBlocking(false); // 維護到集合中 scList.add(sc); } // 遍歷連接,讀取數據 for(SocketChannel temp : scList){ // 創建一個緩存區 ByteBuffer buffer = ByteBuffer.allocate(1 * 1024); // read方法非阻塞,返回值為數據長度 int len = temp.read(buffer); String socketName = temp.toString(); if(len > 0){ System.out.println(Thread.currentThread().getName() + " " + socketName + " receive data size is " + len); ByteBuffer data = ByteBuffer.allocate(len); // 翻轉緩沖區,使得能被put buffer.flip(); data.put(buffer); System.out.println(Thread.currentThread().getName() + " " + socketName + " receive data is " + new String(data.array(), StandardCharsets.UTF_8)); } } } } } ``` 執行結果: ``` java /** * 執行結果: * server listen on 9000 * main client enter java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data size is 3 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data is 123 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data size is 6 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data is 你好 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data size is 6 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data is 中國 * main client enter java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data size is 3 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data is 123 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data size is 3 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data is xxx * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data size is 4 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50039] receive data is exit * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data size is 4 * main java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:50308] receive data is exit */ ``` `NonBlockClientDemo` - 客戶端 ``` java package org.mango.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Scanner; /** * @Description 非阻塞測試 客戶端 * @Date 2021-10-01 15:48 * @Created by mango */ public class NonBlockClientDemo { public static void main(String[] args) throws IOException { SocketChannel sc = SocketChannel.open(); String host = "127.0.0.1"; int port = 9000; boolean isConnect = sc.connect(new InetSocketAddress(host,port)); if(isConnect){ System.out.println("connect server " + host + ":" + port + " success!"); // 從輸入流中讀取數據 Scanner scanner = new Scanner(System.in); System.out.println("輸入exit:客戶端退出;其他為發送數據!"); boolean loop = true; while (loop){ System.out.print("請輸入:"); String input = scanner.next(); ByteBuffer data = ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8)); // 發送數據 sc.write(data); if("exit".equals(input)){ loop = false; } } } System.out.println("client exit!"); } } ``` 執行結果: ``` java /** * 執行結果1: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:123 * 請輸入:你好 * 請輸入:中國 * 請輸入:exit * client exit! */ ``` ``` java /** * 執行結果2: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:123 * 請輸入:xxx * 請輸入:exit * client exit! */ ``` ### 多線程版本 `MTNonBlockServerDemo` - 服務端 ``` java package org.mango.demo.mtnio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; /** * @Description (服務端非阻塞)主動遍歷 一線程1連接,數據讀取 * @Date 2021-10-01 15:26 * @Created by mango */ public class MTNonBlockServerDemo { public static void main(String[] args) throws IOException, InterruptedException { // 服務端,使用ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); int port = 9000; // 綁定本地端口 serverSocketChannel.bind(new InetSocketAddress(port)); System.out.println("server listen on " + port); // 設置服務端通道非阻塞 serverSocketChannel.configureBlocking(false); while (true){ // accept方法非阻塞,立即返回,null表示沒有client連接進來 SocketChannel sc = serverSocketChannel.accept(); if(null == sc){ Thread.sleep(200); }else{ System.out.println(Thread.currentThread().getName() + " client enter " + sc.toString()); // 設置連接非阻塞 sc.configureBlocking(false); new WorkThread(sc).start(); } } } } /** * 工作線程 */ class WorkThread extends Thread{ private SocketChannel socketChannel; public WorkThread(SocketChannel socketChannel){ this.socketChannel = socketChannel; } @Override public void run() { boolean loop = true; String tn = Thread.currentThread().getName(); String sn = socketChannel.toString(); try{ // 將接收到的數據后跟 處理線程名稱 發送給客戶端 while(loop){ ByteBuffer buffer = ByteBuffer.allocate(1024); // read() 不會阻塞 int len = socketChannel.read(buffer); if(len > 0) { System.out.println(tn + ":" + sn + " receive data size :" + len); ByteBuffer data = ByteBuffer.allocate(len); // 翻轉緩沖區,使得能被put buffer.flip(); data.put(buffer); String sData = new String(data.array(),StandardCharsets.UTF_8); System.out.println(tn + ":" + sn + " receive data:" + sData); String rData = sData + ":" + tn; socketChannel.write(ByteBuffer.wrap(rData.getBytes(StandardCharsets.UTF_8))); } } } catch (IOException e) { // 異常退出 try { socketChannel.close(); } catch (IOException e1) { e1.printStackTrace(); } System.out.println(tn + ":" + sn + " exception close!"); } } } ``` ``` java /** * 執行結果: * server listen on 9000 * main client enter java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data size :3 * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data:123 * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data size :6 * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data:你好 * main client enter java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data size :6 * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data:中國 * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data size :3 * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data:123 * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data size :4 * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] receive data:exit * Thread-1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54480] exception close! * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data size :4 * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] receive data:exit * Thread-0:java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:54386] exception close! */ ``` `MTNonBlockClientDemo` - 客戶端 ``` java package org.mango.demo.mtnio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Scanner; /** * @Date 2021-10-03 15:26 * @Created by mango */ public class MTNonBlockClientDemo { public static void main(String[] args) throws IOException, InterruptedException { SocketChannel sc = SocketChannel.open(); String host = "127.0.0.1"; int port = 9000; sc.connect(new InetSocketAddress(host,port)); // 設置非阻塞 sc.configureBlocking(false); System.out.println("connect server " + host + ":" + port + " success!"); // 從輸入流中讀取數據 Scanner scanner = new Scanner(System.in); System.out.println("輸入exit:客戶端退出;其他為發送數據!"); boolean loop = true; while (loop){ System.out.print("請輸入:"); String input = scanner.next(); ByteBuffer data = ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8)); // 發送數據 sc.write(data); if("exit".equals(input)){ loop = false; } } System.out.println("client exit"); } } ``` 執行結果: ``` java /** * 執行結果1: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:123 * 請輸入:你好 * 請輸入:exit * client exit */ ``` ``` java /** * 執行結果2: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:中國 * 請輸入:123 * 請輸入:exit * client exit */ ``` ***總結:*** ***1. 通過channel的configureBlocking(flase)設置連接為非阻塞后,accept()或者read()方法就會直接返回*** ***2. 1連接1線程雖然能提高服務端處理客戶端并發連接數,但是線程資源有限,不易多開。因此多線程版本再優化就可以放到線程池管理。*** ***3. 將線程用線程池管理起來,就能改為線程池版本非阻塞IO模型。*** ``` java // 創建工作線程池 ExecutorService executorService = Executors.newFixedThreadPool(8); // 提交到線程池 executorService.submit(new WorkThread(sc)); ``` ### 多路復用器版本 ![](https://img.kancloud.cn/f9/13/f913f0955e177437e9d6c02af5ef7c29_2140x1286.png) `SelectNIOServerDemo` - 服務端 ``` java package org.mango.demo.selectnio; import java.io.IOException; import java.net.InetSocketAddress; 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.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Set; /** * @Description 多路復用器版本 服務端 * @Date 2021-10-03 13:40 * @Created by mango */ public class SelectNIOServerDemo { public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); int port = 9000; serverSocketChannel.bind(new InetSocketAddress(port)); serverSocketChannel.configureBlocking(false); System.out.println("server listen on port " + port); // 設置多路復用器 Selector selector = Selector.open(); // 將serverSocketChannel的accept事件注冊到多路復用器 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true){ // 查看selector.select()是否有事件準備就緒 selector.select(); Set<SelectionKey> selectionKeySet = selector.selectedKeys(); Iterator<SelectionKey> iterable = selectionKeySet.iterator(); while(iterable.hasNext()){ SelectionKey selectionKey = iterable.next(); // 將事件remove掉,防止重復處理 iterable.remove(); if(selectionKey.isAcceptable()){ // 得到連接的通道 SocketChannel socketChannel = serverSocketChannel.accept(); String socketName = socketChannel.toString(); System.out.println(socketName + " connect success!"); // 設置為非阻塞 socketChannel.configureBlocking(false); // 注冊讀寫事件到多路復用器 socketChannel.register(selector,SelectionKey.OP_READ); // socketChannel.register(selector,SelectionKey.OP_WRITE); }else if(selectionKey.isReadable()){ // 得到可讀的通道 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); String socketName = socketChannel.toString(); try { // 讀取數據并回寫數據給客戶端 ByteBuffer buffer = ByteBuffer.allocate(1024); int len = socketChannel.read(buffer); if (len == -1) { System.out.println(socketName + " closed!"); } if (len > 0) { ByteBuffer data = ByteBuffer.allocate(len); buffer.flip(); data.put(buffer); String sData = new String(data.array(), StandardCharsets.UTF_8); System.out.println(socketName + " receive data:" + sData); // 回寫數據 String tn = Thread.currentThread().getName(); String rData = sData + " - " + tn; socketChannel.write(ByteBuffer.wrap(rData.getBytes(StandardCharsets.UTF_8))); } }catch (Exception e){ // 異常推出 socketChannel.close(); System.out.println(socketName + " exception close!"); } } } } } } ``` 執行結果: ``` java /** * 執行結果: * server listen on port 9000 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] connect success! * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] receive data:123 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] receive data:你好 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] receive data:中國 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:58056] connect success! * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:58056] receive data:你呀 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:58056] receive data:搞毛線哦 * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:58056] receive data:exit * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:58056] exception close! * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] receive data:exit * java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:57934] exception close! */ ``` `SelectNIOClientDemo` - 服務端 ``` java package org.mango.demo.selectnio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Scanner; import java.util.Set; /** * @Date 2021-10-03 15:26 * @Created by mango */ public class SelectNIOClientDemo { public static void main(String[] args) throws IOException, InterruptedException { SocketChannel sc = SocketChannel.open(); String host = "127.0.0.1"; int port = 9000; sc.connect(new InetSocketAddress(host,port)); // 設置非阻塞 sc.configureBlocking(false); System.out.println("connect server " + host + ":" + port + " success!"); // 設置多路復用器,注冊讀事件 Selector selector = Selector.open(); sc.register(selector, SelectionKey.OP_READ); sc.register(selector, SelectionKey.OP_WRITE); // 從輸入流中讀取數據 Scanner scanner = new Scanner(System.in); System.out.println("輸入exit:客戶端退出;其他為發送數據!"); boolean loop = true; while (loop){ // 查看多路復用器 selector.select(); Set<SelectionKey> selectionKeySet = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeySet.iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); // 移除掉事件,防止重復處理 iterator.remove(); if(selectionKey.isWritable()){ System.out.print("請輸入:"); String input = scanner.next(); ByteBuffer data = ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8)); // 發送數據 sc.write(data); if("exit".equals(input)){ loop = false; } }else if(selectionKey.isReadable()){ // 得到可讀的通道 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); String socketName = socketChannel.toString(); // 讀取數據并回寫數據給客戶端 ByteBuffer buffer = ByteBuffer.allocate(1024); int len = socketChannel.read(buffer); if(len == -1){ System.out.println(socketName + " closed!"); } if(len > 0){ ByteBuffer data = ByteBuffer.allocate(len); buffer.flip(); data.put(buffer); String sData = new String(data.array(), StandardCharsets.UTF_8); System.out.println(socketName + " receive data:" + sData); } } } } // 關閉資源 selector.close(); sc.close(); System.out.println("client exit"); } } ``` 執行結果: ``` java /** * 執行結果1: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:你呀 * 請輸入:搞毛線哦 * 請輸入:exit * client exit */ ``` ``` java /** * 執行結果2: * connect server 127.0.0.1:9000 success! * 輸入exit:客戶端退出;其他為發送數據! * 請輸入:123 * 請輸入:你好 * 請輸入:中國 * 請輸入:exit * client exit */ ``` ***總結*** ***1. 使用Selector選擇器來實現多路復用器,調用select()方法得到就緒事件。*** ***2. socket關閉時,多路復用器會收到read事件,read到的字節-1。***
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看