NIO與Socket
阿新 • • 發佈:2018-11-14
一、Socket 的使用
1、單執行緒Socket的使用
/** * 單執行緒版本 * 問題描述:只能服務單個客戶端 * 解決方案:多執行緒版本 */ public class Socket_V1 { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(1234); System.out.println("啟動伺服器:"); while(true) { System.out.println("等待客戶端連結(阻塞)……"); Socket socket = serverSocket.accept(); System.out.println("有連結進來了……"); handle(socket); } } private static void handle(Socket socket) { try { InputStream inputStream = socket.getInputStream(); byte[] b = new byte[1024]; String str = null; while(true) { str = null; System.out.println("等待輸入……"); int read = inputStream.read(b); if(read == -1) { System.out.println("客戶端關閉。"); break; } str = new String(b); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } }
2、多執行緒版本
/** * 多執行緒版本 * 問題描述:多執行緒開銷大 * 解決方案:NIO */ public class Socket_V2 { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(1234); // 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。 ExecutorService threadPool = Executors.newFixedThreadPool(10); System.out.println("啟動伺服器:"); while(true) { System.out.println("等待客戶端連結(阻塞)……"); final Socket socket = serverSocket.accept(); System.out.println("有連結進來了……"); //使用執行緒池來,支援併發 threadPool.execute(new Runnable() { public void run() { handle(socket); } }); } } private static void handle(Socket socket) { try { InputStream inputStream = socket.getInputStream(); byte[] b = new byte[1024]; String str = null; while(true) { str = null; System.out.println("等待輸入……"); int read = inputStream.read(b); if(read == -1) { System.out.println("客戶端關閉。"); break; } str = new String(b); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } }
3、NIO
/** * NIO版本 * 原理:IO多路複用 * NIO與舊Socket對比: * ServerSocketChannel <-等價於-> ServerSocket * SocketChannel <-等價於-> Socket * Selector 選擇器 * SelectionKey 事件型別 */ public class Socket_NIO { public static void main(String[] args) throws Exception { //獲得Socket通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //將通道設定為非阻塞模式 serverSocketChannel.configureBlocking(false); //將通道繫結到指定的埠 serverSocketChannel.socket().bind(new InetSocketAddress(1234)); //獲取通道選擇器 Selector selector = Selector.open(); //將連線事件,註冊到選擇器內(步驟1) serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("伺服器啟動成功。埠已經監聽……"); while(true) { selector.select();//阻塞等待已經註冊的事件 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while(iterator.hasNext()) { SelectionKey next = iterator.next(); iterator.remove(); if(next.isAcceptable()) { //如果是連線事件,註冊讀寫事件 System.out.println("獲取到連線事件。"); ServerSocketChannel sschannel = (ServerSocketChannel) next.channel(); //該強轉是步驟1記憶體放的值 SocketChannel channel = sschannel.accept(); //獲取客戶端連線的通道 channel.configureBlocking(false);//設定為非阻塞 channel.register(selector, SelectionKey.OP_READ); //步驟2 }else if(next.isReadable()) { //如果是可讀事件,則執行讀操作 System.out.println("獲取到可讀事件。"); SocketChannel channel = (SocketChannel) next.channel(); //獲取socket通道,該強轉是步驟2記憶體放的值 ByteBuffer dst = ByteBuffer.allocate(1024); //緩衝區 int read = channel.read(dst); if(read < 0 ) { System.out.println("客戶端關閉。"); next.cancel(); break; } System.out.println("客戶端輸入:"+new String(dst.array())); } //還有其他事件……略 } } } }