Java NIO小案例
阿新 • • 發佈:2018-12-25
package com.gsau.NIO; 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.util.Iterator; /** * @Description: 建立NIOServer * @Date: 2018/9/12 13:16 * @author: wgq * @version: 1.0 * @param: */ public class NIOServer { private Selector selector; // 通道管理器 /** * @Description: 獲得一個通道並且做一些初始化的工作(為他註冊一個Selector) * @Date: 2018/9/12 13:17 * @author: wgq * @version: 1.0 * @param: */ public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 獲得一個ServerSocket通道,相當於傳統socket中的ServerSocket serverChannel.configureBlocking(false); // 設定通道為非阻塞 serverChannel.socket().bind(new InetSocketAddress(port)); // 將該通道對應的ServerSocket繫結到port埠,ip預設就是本機的ip this.selector = Selector.open(); // 獲得一個通道管理器 serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 將通道管理器和該通道繫結,併為該通道註冊SelectionKey.OP_ACCEPT事件,註冊該事件後,當該事件到達時selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞 } /** * @Description: 採用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理 * @Date: 2018/9/12 13:19 * @author: wgq * @version: 1.0 * @param: */ public void listen() throws IOException { System.out.println("服務端啟動成功!"); while (true) { // 輪詢訪問selector selector.select(); // 當註冊的事件到達時,方法返回;否則,該方法會一直阻塞,當一個事件到達的時候他會自動監聽到 Iterator<?> ite = this.selector.selectedKeys().iterator(); // 獲得selector中選中的項的迭代器,選中的項為註冊的事件 while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); //獲取選中的KEY ite.remove(); // 刪除已選的key,以防重複處理 handler(key); } } } /** * @Description: 處理請求的控制代碼 * @Date: 2018/9/12 13:19 * @author: wgq * @version: 1.0 * @param: */ public void handler(SelectionKey key) throws IOException { if (key.isAcceptable()) { // 客戶端請求連線事件 handlerAccept(key); } else if (key.isReadable()) { // 獲得可讀的事件 handelerRead(key); } } /** * @Description: 處理連線的請求 * @Date: 2018/9/12 13:22 * @author: wgq * @version: 1.0 * @param: */ 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); // 在和客戶端連線成功之後,為了可以接收到客戶端的資訊,需要給通道設定讀的許可權。 } /** * @Description: 處理讀的事件 * ByteBuffer:ByteBuffer的讀寫模式是分開的 * 建立方式: * 1.ByteBuffer buffer=ByteBuffer.allocate(256); * 2.ByteBuffer buffer=ByteBuffer.wrap(byteArray);這裡的byteArray可以包含了資料,相當於寫入了資料到緩衝區 * 寫入資料: * 1.ByteBuffer buffer=ByteBuffer.wrap(byteArray) * 2.buffer.put(bytes) * 清除快取區: * buffer.clear(); 這個方法實際上也不會改變緩衝區的資料,而只是簡單的重置了緩衝區的主要索引值,不必為了每次讀寫都建立新的緩衝區,那樣做會降低效能。 * 相反,要重用現在的緩衝區,在再次讀取之前要清除緩衝區。 * 讀取資料: * 呼叫buffer.get(bytes); * @Date: 2018/9/12 13:20 * @author: wgq * @version: 1.0 * @param: */ public void handelerRead(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); // 伺服器可讀取訊息:得到事件發生的Socket通道(其實就是這個請求對應的通道) 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(); } } /** * @Description: 啟動服務端進行測試 * @Date: 2018/9/12 13:26 * @author: wgq * @version: 1.0 * @param: */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }