傳統IO;
package OIO; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 傳統socket服務端 */ public class OioServer { @SuppressWarnings("resource") public static void main(String[] args) throws Exception { ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); //創建socket服務,監聽10101端口 ServerSocket server=new ServerSocket(10101); System.out.println("服務器啟動!"); while(true){ //阻塞 final Socket socket = server.accept();//有一個客戶端進來就向下執行,長連接的服務器。 //用線程池可以有多個客戶端連接,但是非常消耗性能 //tomcat里面來一個請求就開一個線程,當一問一答結束,服務端主動關閉,那么這個線程就可以為其他請求服務了。 System.out.println("來個一個新客戶端!"); newCachedThreadPool.execute(new Runnable() { @Override public void run() { handler(socket); } }); } } /** * 讀取數據 */ public static void handler(Socket socket){ try { byte[] bytes = new byte[1024]; InputStream inputStream = socket.getInputStream();//獲取輸入流 while(true){ //阻塞 int read = inputStream.read(bytes); if(read != -1){// -1就是客戶端關閉了。 System.out.println(new String(bytes, 0, read)); }else{ break; } } } catch (Exception e) { e.printStackTrace(); }finally{ try { System.out.println("socket關閉"); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
NIO:
package 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; /** * NIO服務端 */ public class NIOServer { // 通道管理器 private Selector selector; /** * 獲得一個ServerSocket通道,並對該通道做一些初始化的工作 */ public void initServer(int port) throws IOException { // 獲得一個ServerSocket通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 設置通道為非阻塞,只能是非阻塞的。 serverChannel.configureBlocking(false); // 將該通道對應的ServerSocket綁定到port端口 serverChannel.socket().bind(new InetSocketAddress(port)); // 獲得一個通道管理器 this.selector = Selector.open(); // 將通道管理器和該通道綁定,並為該通道注冊SelectionKey.OP_ACCEPT事件,注冊該事件后, // 當該事件到達時,selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞。 //注冊請求連接進來的key : OP_ACCEPT serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * 采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理 */ public void listen() throws IOException { System.out.println("服務端啟動成功!"); // 輪詢訪問selector //客戶端連接進來和發送數據的請求,都走這里,這里是死循環。 while (true) { // 當注冊的事件到達時,方法返回;否則,該方法會一直阻塞, //客戶端連接進來和發送數據的請求,都走這里,阻塞 selector.select();//select是C實現的,多路復用的實現。 // 獲得selector中選中的項的迭代器,選中的項為注冊的事件 Iterator<?> ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 刪除已選的key,以防重復處理 ite.remove(); handler(key); } } } /** * 處理請求,客戶端連接進來和客戶端發數據,走這里。 */ public void handler(SelectionKey key) throws IOException { // 客戶端連接進來 if (key.isAcceptable()) { handlerAccept(key); // 客戶端發數據 } else if (key.isReadable()) { handelerRead(key); } } /** * 處理連接請求 */ public void handlerAccept(SelectionKey key) throws IOException { ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 獲得和客戶端連接的通道, SocketChannel channel = server.accept(); // 設置成非阻塞 channel.configureBlocking(false); // 在這里可以給客戶端發送信息哦 System.out.println("新的客戶端連接"); // 在和客戶端連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限。 //注冊接收消息的key : OP_READ channel.register(this.selector, SelectionKey.OP_READ); } /** * 處理讀的事件 */ public void handelerRead(SelectionKey key) throws IOException { // 服務器可讀取消息:得到事件發生的Socket通道 SocketChannel channel = (SocketChannel) key.channel(); // 創建讀取的緩沖區 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(); } } /** * 啟動服務端測試 */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }
Netty入門教程 第一天內容 傳統IO與NIO比較 傳送IO特點 =======================分割線========================== NIO的新API ServerSocketChannel,對應傳統IO的: ServerSocket SocketChannel,對應傳統IO的: Socket Selector:NIO核心的東西,負責監聽ServerSocketChannel、SocketChannel。NIO是可以實現單線程為多個客戶端服務的。傳統IO是做不到的, 傳統IO要多線程才行。 SelectionKey:監聽的事件。 NIO的一些疑問 1、客戶端關閉的時候會拋出異常,死循環 解決方案 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(); } 2、selector.select();阻塞,那為什么說nio是非阻塞的IO? selector.select() selector.select(1000);阻塞1秒,1秒還沒有請求過來就返回了, selector.wakeup();也可以喚醒selector selector.selectNow();也可以立馬返還,視頻里忘了講了,哈,這里補上 3、SelectionKey.OP_WRITE是代表什么意思:OP_WRITE表示底層緩沖區是否有空間(事件的觸發條件),是則響應返還true,一般不注冊這個事件。