前言
自己一直没有去了解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(); } } }