前言
自己一直沒有去了解IO方面的知識,對於IO(包括Socket編程)都很模糊,通過一段時間的學習,有所了解並記錄如下。
github代碼地址:https://github.com/Chenrencun/io-learn
正文
一、BIO(同步阻塞IO)
1、同步阻塞IO,服務器端的實現模式是一個連接建立一個線程。也就是說,當客戶端有連接請求時,服務器端會啟動一個線程去處理。假如這個連接不做任何事,就會導致一些不必要的開銷,可以通過線程池改善。
2、實現:
(1)單線程:
①服務器端:
public static void server(){ ServerSocket serverSocket = null; Socket socket = null; InputStream inputStream = null; InputStreamReader inputStreamReader = null;char[] chars = new char[1024]; try { serverSocket = new ServerSocket(8888); System.out.println("**********服務器即將啟動,等待客戶端的連接*************"); while (true){ socket = serverSocket.accept(); // 連接建立好之后,從socket中獲取輸入流 inputStream = socket.getInputStream(); inputStreamReader = new InputStreamReader(inputStream); inputStreamReader.read(chars); System.out.println("客戶端說:" + new String(chars, 0 ,chars.length)); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); inputStream.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
②客戶端:
public static void client(){ Socket socket = null; OutputStream outputStream = null; OutputStreamWriter outputStreamWriter = null; try { socket = new Socket("localhost", 8888); outputStream = socket.getOutputStream(); outputStreamWriter = new OutputStreamWriter(outputStream); outputStreamWriter.write("This is Bio!"); outputStreamWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStreamWriter.close(); outputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
(2)多線程:
①服務端:
public static void server(){ ServerSocket serverSocket = null; Socket socket = null; try { serverSocket = new ServerSocket(8888); System.out.println("**********服務器即將啟動,等待客戶端的連接*************"); while (true){ socket = serverSocket.accept(); // 連接建立好之后,每個連接新建一個線程去處理 new Thread(new MyRunnable(socket)).start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } // 內部類(處理客戶端請求) static class MyRunnable implements Runnable{ private Socket socket; public MyRunnable(Socket socket){ this.socket=socket; } @Override public void run() { InputStream inputStream = null; InputStreamReader inputStreamReader = null; char[] chars = new char[1024]; try { inputStream = socket.getInputStream(); inputStreamReader = new InputStreamReader(inputStream); inputStreamReader.read(chars); System.out.println("客戶端說:" + new String(chars, 0 ,chars.length)); } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
②客戶端:
public static void client(){ Socket socket = null; OutputStream outputStream = null; OutputStreamWriter outputStreamWriter = null; try { socket = new Socket("localhost", 8888); outputStream = socket.getOutputStream(); outputStreamWriter = new OutputStreamWriter(outputStream); outputStreamWriter.write("This is Bio!"); outputStreamWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStreamWriter.close(); outputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
(3)線程池:
①服務端:
public static void server(){ ServerSocket serverSocket = null; Socket socket = null; InputStream inputStream = null; InputStreamReader inputStreamReader = null; char[] chars = new char[1024]; try { serverSocket = new ServerSocket(8888); System.out.println("**********服務器即將啟動,等待客戶端的連接*************"); while (true){ socket = serverSocket.accept(); // 通過線程池機制去處理 executorService.submit(new MyRunnable(socket)); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); inputStream.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } // 內部類(處理客戶端請求) static class MyRunnable implements Runnable{ private Socket socket; public MyRunnable(Socket socket){ this.socket=socket; } @Override public void run() { InputStream inputStream = null; InputStreamReader inputStreamReader = null; char[] chars = new char[1024]; try { inputStream = socket.getInputStream(); inputStreamReader = new InputStreamReader(inputStream); inputStreamReader.read(chars); System.out.println("客戶端說:" + new String(chars, 0 ,chars.length)); } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
②客戶端:
public static void client(){ Socket socket = null; OutputStream outputStream = null; OutputStreamWriter outputStreamWriter = null; try { socket = new Socket("localhost", 8888); outputStream = socket.getOutputStream(); outputStreamWriter = new OutputStreamWriter(outputStream); outputStreamWriter.write("This is Bio!"); outputStreamWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStreamWriter.close(); outputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
二、NIO(同步非阻塞IO)
1、同步非阻塞IO,服務器的實現模式是一個IO請求建立一個線程。也就是說,客戶端的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有IO請求時,會啟動一個線程去處理。
2、實現:
(1)單線程:
①服務端:
public static void server(){ ServerSocketChannel serverSocketChannel = null; Selector selector = null; try { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // select方法指定超時時間,非阻塞 if (selector.select(2000) == 0) { System.out.println("*"); } Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); //如果服務端信道感興趣的I/O操作為accept if (selectionKey.isAcceptable()){ SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } //如果客戶端信道感興趣的I/O操作為read if (selectionKey.isReadable()){ SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read = socketChannel.read(buffer); if (read == -1){ // 客戶端關閉 selectionKey.cancel(); } else { // 對客戶端信息處理 System.out.println("客戶端說:" + new String(buffer.array())); } } //這里需要手動從鍵集中移除當前的key iterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); serverSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }
②客戶端:
public static void client(){ SocketChannel socketChannel = null; Selector selector = null; try { selector = Selector.open(); socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("localhost", 8888)); socketChannel.register(selector, SelectionKey.OP_READ); while (!socketChannel.finishConnect()){ System.out.println("正在連接中"); } System.out.println("成功連接到服務端"); // 向服務端發送信息 ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("This is Nio!".getBytes()); buffer.flip(); socketChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }
(2)多線程:
①服務端:
public static void server(){ ServerSocketChannel serverSocketChannel = null; Selector selector = null; try { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // select方法指定超時時間,非阻塞 if (selector.select(2000) == 0) { System.out.println("*"); } Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); //如果服務端信道感興趣的I/O操作為accept if (selectionKey.isAcceptable()){ SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } //如果客戶端信道感興趣的I/O操作為read if (selectionKey.isReadable()){ // 針對每次read,新建一個線程去處理 new Thread(new MyRunnable(selectionKey)).start(); } //這里需要手動從鍵集中移除當前的key iterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); serverSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } static class MyRunnable implements Runnable{ private SelectionKey selectionKey; public MyRunnable(SelectionKey selectionKey){ this.selectionKey = selectionKey; } @Override public void run() { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read = 0; try { read = socketChannel.read(buffer); } catch (IOException e) { e.printStackTrace(); } if (read == -1){ // 客戶端關閉 selectionKey.cancel(); } else { // 對客戶端信息處理 System.out.println("客戶端說:" + new String(buffer.array())); } } }
②客戶端:
public static void client(){ SocketChannel socketChannel = null; Selector selector = null; try { selector = Selector.open(); socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("localhost", 8888)); socketChannel.register(selector, SelectionKey.OP_READ); while (!socketChannel.finishConnect()){ System.out.println("正在連接中"); } System.out.println("成功連接到服務端"); // 向服務端發送信息 ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("This is Nio!".getBytes()); buffer.flip(); socketChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }
(3)線程池:
①服務端:
private static ExecutorService executorService = new ThreadPoolExecutor(3, 5, 3, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy()); public static void server(){ ServerSocketChannel serverSocketChannel = null; Selector selector = null; try { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // select方法指定超時時間,非阻塞 if (selector.select(2000) == 0) { System.out.println("*"); } Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); //如果服務端信道感興趣的I/O操作為accept if (selectionKey.isAcceptable()){ SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } //如果客戶端信道感興趣的I/O操作為read if (selectionKey.isReadable()){ // 針對每次read,使用線程池機制去處理 executorService.submit(new MyRunnable(selectionKey)); } //這里需要手動從鍵集中移除當前的key iterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); serverSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } static class MyRunnable implements Runnable{ private SelectionKey selectionKey; public MyRunnable(SelectionKey selectionKey){ this.selectionKey = selectionKey; } @Override public void run() { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read = 0; try { read = socketChannel.read(buffer); } catch (IOException e) { e.printStackTrace(); } if (read == -1){ // 客戶端關閉 selectionKey.cancel(); } else { // 對客戶端信息處理 System.out.println("客戶端說:" + new String(buffer.array())); } } }
②客戶端:
public static void client(){ SocketChannel socketChannel = null; Selector selector = null; try { selector = Selector.open(); socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("localhost", 8888)); socketChannel.register(selector, SelectionKey.OP_READ); while (!socketChannel.finishConnect()){ System.out.println("正在連接中"); } System.out.println("成功連接到服務端"); // 向服務端發送信息 ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("This is Nio!".getBytes()); buffer.flip(); socketChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } finally { try { selector.close(); socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }