package com.shengsiyuan.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.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.UUID; public class NioServer { private static Map<String, SocketChannel> clientMap = new HashMap(); public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//建立一個ServerSocketChannel serverSocketChannel.configureBlocking(false);//非阻塞 ServerSocket serverSocket = serverSocketChannel.socket();//經過服務端的channel獲取服務端的socket, serverSocket.bind(new InetSocketAddress(8899)); Selector selector = Selector.open();//服務端channel註冊到選擇器上, //選擇器能夠關聯多個channel對象,這裏只有一個channel。 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//如今關注服務端channel的鏈接事件, while (true) { try {//異常try()catch{} selector.select();//阻塞,等着關注的事件發生,返回發生的關注事件的數量。 Set<SelectionKey> selectionKeys = selector.selectedKeys();//一個個的事件,如今只有一個OP_ACCEPT事件, selectionKeys.forEach(selectionKey -> { final SocketChannel client; try { //OP_ACCEPT事件 if (selectionKey.isAcceptable()) { //這個selectionKey關聯的channel是服務端channel,OP_ACCEPT是服務端channel關注的事件(並註冊在選擇器上) //因此這裏能夠強制轉爲服務端channel, ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel(); client = server.accept();//接受鏈接,真正接收鏈接以後返回SocketChannel對象,就是與客戶端通訊的socket, //對於當前通道,服務端socket就用不上了,用SocketChannel client.configureBlocking(false);//非阻塞的 //轉而把SocketChannel註冊到選擇器,並關注讀事件, client.register(selector, SelectionKey.OP_READ); //此時這個選擇器有2個socket,一個服務端channel一個SocketChannel,一個關注鏈接一個關注數據讀取, String key = "【" + UUID.randomUUID().toString() + "】"; clientMap.put(key, client); //OP_READ事件 } else if (selectionKey.isReadable()) { client = (SocketChannel) selectionKey.channel();//確定是客戶端關聯的socket ByteBuffer readBuffer = ByteBuffer.allocate(1024); int count = client.read(readBuffer);//讀到buffer if (count > 0) { readBuffer.flip(); Charset charset = Charset.forName("utf-8");//編碼 //發過來的數據轉成string String receivedMessage = String.valueOf(charset.decode(readBuffer).array()); //打印客戶端發過來的數據 System.out.println(client + ": " + receivedMessage); String senderKey = null; for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) { if (client == entry.getValue()) { senderKey = entry.getKey(); break; } } for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) { SocketChannel value = entry.getValue(); //先寫到ByteBuffer ByteBuffer writeBuffer = ByteBuffer.allocate(1024); writeBuffer.put((senderKey + ": " + receivedMessage).getBytes()); writeBuffer.flip(); //而後buteBuffer寫出去到channel value.write(writeBuffer); } } } } catch (Exception ex) { ex.printStackTrace(); } }); selectionKeys.clear();//事件處理完成以後要清空,不然下次還要處理,就會報空指針。就是iter.remove(); } catch (Exception ex) { ex.printStackTrace(); } } } }
package com.shengsiyuan.nio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; 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.time.LocalDateTime; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NioClient { public static void main(String[] args) throws IOException { try { SocketChannel socketChannel = SocketChannel.open();//@587 socketChannel.configureBlocking(false); Selector selector = Selector.open(); //OP_ACCEPT是接受鏈接,OP_CONNECT是發起鏈接, socketChannel.register(selector, SelectionKey.OP_CONNECT); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8899)); while (true) { selector.select();//阻塞 Set<SelectionKey> keySet = selector.selectedKeys(); for (SelectionKey selectionKey : keySet) { //已經創建好了鏈接 if (selectionKey.isConnectable()) { SocketChannel client = (SocketChannel) selectionKey.channel();//就是以前的那個socket @587 //鏈接是否處於進行狀態 if (client.isConnectionPending()) { client.finishConnect();//完成鏈接,如今鏈接真正創建好了, //向服務器發送鏈接創建好了的消息 ByteBuffer writeBuffer = ByteBuffer.allocate(1024); writeBuffer.put((LocalDateTime.now() + " 鏈接成功").getBytes()); //翻轉 writeBuffer.flip(); //寫到channel client.write(writeBuffer); ExecutorService executorService = Executors.newSingleThreadExecutor( Executors.defaultThreadFactory());//只有一個線程的線程池 executorService.submit(() -> { while (true) { try { writeBuffer.clear(); InputStreamReader input = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(input); String sendMessage = br.readLine(); //寫入到buffer writeBuffer.put(sendMessage.getBytes()); //翻轉 writeBuffer.flip(); //buffer寫出去 client.write(writeBuffer); } catch (Exception ex) { ex.printStackTrace(); } } }); } //給這個channel註冊讀取事件, client.register(selector, SelectionKey.OP_READ);//@587 } else if (selectionKey.isReadable()) { SocketChannel client = (SocketChannel) selectionKey.channel();//@587 ByteBuffer readBuffer = ByteBuffer.allocate(1024); int count = client.read(readBuffer); if (count > 0) { String receivedMessage = new String(readBuffer.array(), 0, count);//字節數組轉字符串 System.out.println(receivedMessage); } } } keySet.clear();//清除事件,就是清除SelectionKey集合。 } } catch (Exception ex) { ex.printStackTrace(); } } }