NIO的前世今生
来源:互联网 发布:霍建华扒皮知乎 编辑:程序博客网 时间:2024/06/05 11:04
在面试中NIO的东西还是有很多会被面到,那么NIO是什么?
从古老的套接字(socket)说起,传统的socket编程为新启动一个服务,阻塞到这,一个Client端连接过来,new 一个Thread 去处理
总结顺便学习一下IDEA ,破解版http://xidea.online,
file -> settings -> build,exception,deployment -> compiler -> java compiler
主函数 psvm +tab键 输出 so+tab
1.BIO 即原始的同步阻塞式
服务端
package com.wpx.bio;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/** * 服务端 通过一个端口对外提供服务 * 当一个Client端连接后通过新启动一个线程去处理 */class Server { final static int PORT = 8888; public static void main(String[] args) { ServerSocket ss = null; try { ss = new ServerSocket(PORT); System.out.println("Server start...."); Socket socket = ss.accept(); //new Thread Handler new Thread(new ServerHandler(socket)).start(); } catch (IOException e) { e.printStackTrace(); }finally { if(ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } ss = null; } }}
服务端处理类
package com.wpx.bio;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;/**\ * 服务端处理类需要jichengThread或者实现Runnable */public class ServerHandler implements Runnable{private Socket socket ;/** * 构造 Alt+Insert * @param socket */public ServerHandler(Socket socket){this.socket = socket;}/** * Ctrl+O */@Overridepublic void run() {BufferedReader in = null;PrintWriter out = null;try {in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));out = new PrintWriter(this.socket.getOutputStream(), true);String body = null;while(true){body = in.readLine();if(body == null) break;System.out.println("Server :" + body);out.println("服务器端回送响的应数据.");}} catch (Exception e) {e.printStackTrace();} finally {if(in != null){try {in.close();} catch (IOException e) {e.printStackTrace();}}if(out != null){try {out.close();} catch (Exception e) {e.printStackTrace();}}if(socket != null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}socket = null;}}}客户端
package com.wpx.bio;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/** * 服务端 通过一个端口对外提供服务 * 当一个Client端连接后通过新启动一个线程去处理 */class Server { final static int PORT = 8888; public static void main(String[] args) { ServerSocket ss = null; try { ss = new ServerSocket(PORT); System.out.println("Server start...."); Socket socket = ss.accept(); //new Thread Handler new Thread(new ServerHandler(socket)).start(); } catch (IOException e) { e.printStackTrace(); }finally { if(ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } ss = null; } }}因为每一个客户端的连接都会新启动一个线程,给服务器造成了巨大压力,在没有NIO之前,通过使用线程池+一个阻塞的队列来缓解服务器压力
服务端
package com.wpx.bio2;import java.io.BufferedReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.nio.Buffer;/** * 与开始处理思路大体一致 * 不同的是新的客户端连接后偶交由线程池处理 */public class Server { final static int PORT=8888; public static void main(String[] args) { ServerSocket ss=null; BufferedReader in=null; PrintWriter out=null; try{ ss=new ServerSocket(PORT); System.out.println("Server satrt....."); Socket socket = null; HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000); while(true){ socket = ss.accept(); executorPool.execute(new ServerHandler(socket)); } }catch (Exception e){ e.printStackTrace(); new RuntimeException("服务端异常"); }finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(ss != null){ try { ss.close(); } catch (Exception e3) { e3.printStackTrace(); } } ss = null; } }}线程池
package com.wpx.bio2;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class HandlerExecutorPool { private ExecutorService executor; public HandlerExecutorPool(int maxPoolSize, int queueSize){ this.executor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize)); } public void execute(Runnable task){ this.executor.execute(task); }}处理类
package com.wpx.bio2;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class ServerHandler implements Runnable{ private Socket socket; public ServerHandler(Socket socket) { this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(), true); String body = null; while(true){ body = in.readLine(); if(body == null) break; System.out.println("Server:" + body); out.println("Server response"); } } catch (Exception e) { e.printStackTrace(); } finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (Exception e3) { e3.printStackTrace(); } } socket = null; } }}客户端
package com.wpx.bio2;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class Client { final static String ADDRESS = "127.0.0.1"; final static int PORT =8888; public static void main(String[] args) { Socket socket=null; BufferedReader in=null; PrintWriter out=null; try{ socket=new Socket(ADDRESS,PORT ); in=new BufferedReader(new InputStreamReader(socket.getInputStream())); out=new PrintWriter(socket.getOutputStream()); out.print("Client Request"); String response= in.readLine(); System.out.println("Clent"+response); }catch (Exception e){ e.printStackTrace(); }finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (Exception e3) { e3.printStackTrace(); } } socket = null; } }}2.NIO1.0位同步非阻塞
通道:Channel,通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。
缓冲区:原来传输为直练传输,现在加入缓冲区,类似一个快递中转站,快递到了通知你去哪,而不需要一直等待看着快递来
package com.wpx.nio;import java.nio.IntBuffer;public class BufferTest { public static void main(String[] args) { //创建指定长度的缓存区 Buffre不仅仅只有Int类型的,仅依次为了例 //创建指定长度 /** * 生成返回值类型 Alt+Enter */ IntBuffer buf = IntBuffer.allocate(10); buf.put(1);// position位置:0 - > 1 buf.put(2);// position位置:1 - > 2 buf.put(3);// position位置:2 - > 3 //把位置复位为0,也就是position位置:3 - > 0 buf.flip(); System.out.println("容量为 "+buf.capacity()); //容量初始化后不允许发生改变(warp方法包裹数组除外) System.out.println("限制为"+buf.limit()); //实际装载元素 System.out.println("获取下标为1的元素:" + buf.get(1)); System.out.println("get(index)方法,position位置不改变:" + buf); buf.put(1, 4); System.out.println("put(index, change)方法,position位置不变:" + buf);; for (int i = 0; i < buf.limit(); i++) { //调用get方法会使其缓冲区位置(position)向后递增一位 System.out.print(buf.get() + "\t"); } System.out.println("buf对象遍历之后为: " + buf); // 2 wrap方法使用 /** // wrap方法会包裹一个数组: 一般这种用法不会先初始化缓存对象的长度,因为没有意义,最后还会被wrap所包裹的数组覆盖掉。 // 并且wrap方法修改缓冲区对象的时候,数组本身也会跟着发生变化。 int[] arr = new int[]{1,2,5}; IntBuffer buf1 = IntBuffer.wrap(arr); System.out.println(buf1); IntBuffer buf2 = IntBuffer.wrap(arr, 0 , 2); //这样使用表示容量为数组arr的长度,但是可操作的元素只有实际进入缓存区的元素长度 System.out.println(buf2); */ // 3 其他方法 /** IntBuffer buf1 = IntBuffer.allocate(10); int[] arr = new int[]{1,2,5}; buf1.put(arr); System.out.println(buf1); //一种复制方法 IntBuffer buf3 = buf1.duplicate(); System.out.println(buf3); //设置buf1的位置属性 //buf1.position(0); buf1.flip(); System.out.println(buf1); System.out.println("可读数据为:" + buf1.remaining()); int[] arr2 = new int[buf1.remaining()]; //将缓冲区数据放入arr2数组中去 buf1.get(arr2); for(int i : arr2){ System.out.print(Integer.toString(i) + ","); } */ } }
selector:选择器,他是NIO编程的基础,多路复用器提供选择已经再绪的任务的能力
Selector会不断的轮询注册在其上的通道(Channel),如果如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel集合,从而进行后续的IO操作
一个多路复用器(Selector)可以负责成千上万的Channel通道没有上限,这也是JDK使用epoll代替的了传统的select实现,获得连接句柄没有限制,这也就意味着我们只要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这是JDK NIO库的巨大进步
服务端
package com.wpx.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;/** *必须实现Runnable接口 * 新启动一个线程让Selector处于一种轮询的状态 * * @author wangpx */public class Server implements Runnable{ //多路复用器 private Selector selector; //建立缓存区 2的倍数更高效 private ByteBuffer readBuf=ByteBuffer.allocate(1024); private ByteBuffer writeBuf=ByteBuffer.allocate(1024); public Server(int port) { try{ //打开多路复用器 this.selector=Selector.open(); //打开服务通道 ServerSocketChannel ssc = ServerSocketChannel.open(); //将其设置为非阻塞莫斯 ssc.configureBlocking(false); //绑定地址 ssc.bind( new InetSocketAddress(port)); //将服务器通道注册到多路复用器上,并且监听阻塞事件 ssc.register(this.selector, SelectionKey.OP_ACCEPT); System.out.println("Server start and port is :" +port); }catch(Exception e){ e.printStackTrace(); } } @Override public void run() { while(true) { try { // 让多路复用器开始监听 this.selector.select(); //返回多路复用器已经被选择的结果集 Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator(); //进行遍历 while(keys.hasNext()) { //获取一个选择的元素 SelectionKey key = keys.next(); //直接从容器中移除就可以了 keys.remove(); //如果是有效的 if(key.isValid()){ //如果为阻塞状态 if(key.isAcceptable()){ this.accept(key); } //如果是可读状态 if (key.isReadable()){ this.read(key); } //如果是可写状态 if(key.isWritable()){ this.write(key); } } } } catch (IOException e) { e.printStackTrace(); } } } private void write(SelectionKey key) { } private void read(SelectionKey key) { try{ //清空缓冲区的数据 this.readBuf.clear(); //获取之前注册的socket通道对象 SocketChannel sc= (SocketChannel) key.channel(); int read = sc.read(this.readBuf); if (read == -1){ key.channel().close(); key.cancel(); return ; } //有数据则进行读取,读取前要复位方法,详情见上面的demo this.readBuf.flip(); //根据缓冲区的数据长度创建相对的byte数组,接受缓冲区的数据 byte[] bytes=new byte[this.readBuf.remaining()]; //接受缓冲区数据 this.readBuf.get(bytes); //打印结果 String body=new String(bytes).trim(); System.out.println("Server: "+body); }catch(Exception e){ e.printStackTrace(); } } private void accept(SelectionKey key) { try{ //获取服务通道 ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //执行阻塞方法 SocketChannel sc = ssc.accept(); //设置阻塞模式 sc.configureBlocking(false); //注册到多路复用器上并设置读取标识别 sc.register(this.selector,SelectionKey.OP_READ); }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { new Thread(new Server(8888)).start(); }}客户端
package com.wpx.nio;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class Client { public static void main(String[] args) { InetSocketAddress address=new InetSocketAddress("127.0.0.1",8888); //声明连接通道 SocketChannel sc=null; //建立缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); try{ //打开通道 sc = SocketChannel.open(); sc.connect(address); while(true) { //定义一个字节数组,然后使用系统录入功能 byte[] bytes=new byte[1024]; System.in.read(bytes); //把数据放到缓冲 buf.put(bytes); //对缓冲区进行复位 buf.flip(); //写出数据 sc.write(buf); //清空缓冲数据 buf.clear(); } }catch (Exception e){ e.printStackTrace(); }finally { if ( sc!=null){ try{ sc.close(); }catch(Exception e){ sc=null; } } } }}
3.AIO 异步阻塞式
将复杂的Selector屏蔽,并且引入了线程组
服务端
package com.wpx.aio;import java.net.InetSocketAddress;import java.nio.channels.AsynchronousChannelGroup;import java.nio.channels.AsynchronousServerSocketChannel;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Server { //线程池 private ExecutorService executorService; //线程组 private AsynchronousChannelGroup asynchronousChannelGroup; //服务器通道 public AsynchronousServerSocketChannel asynchronousServerSocketChannel; public Server(int port) { try{ //创建一个缓存池 executorService= Executors.newCachedThreadPool(); //创建一个线程组 asynchronousChannelGroup=AsynchronousChannelGroup.withCachedThreadPool(executorService,1); //创建服务器通道 asynchronousServerSocketChannel=AsynchronousServerSocketChannel.open(asynchronousChannelGroup); //进行绑定 asynchronousServerSocketChannel.bind(new InetSocketAddress(port)); System.out.println("server start , port : " + port); //进行阻塞 asynchronousServerSocketChannel.accept(this, new ServerCompletionHandler()); //一直阻塞 不让服务器停止 Thread.sleep(Integer.MAX_VALUE); }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) { Server s=new Server(8888); }}
处理类
package com.wpx.aio;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.nio.channels.CompletionHandler;import java.util.concurrent.ExecutionException;public class ServerCompletionHandler implements CompletionHandler<AsynchronousSocketChannel,com.wpx.aio.Server>{ @Override public void completed(AsynchronousSocketChannel asc, Server attachment) { //当有下一个客户端接入的时候 直接调用Server的accept方法,这样反复执行下去,保证多个客户端可以阻塞 attachment.asynchronousServerSocketChannel.accept(attachment,this); read(asc); } private void read(final AsynchronousSocketChannel asc) { //读取数据 ByteBuffer buf = ByteBuffer.allocate(1024); asc.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer resultSize, ByteBuffer attachment) { //进行读取之后,重置标识位 attachment.flip(); //获得读取的字节数 System.out.println("Server -> " + "收到客户端的数据长度为:" + resultSize); //获取读取的数据 String resultData = new String(attachment.array()).trim(); System.out.println("Server -> " + "收到客户端的数据信息为:" + resultData); String response = "服务器响应, 收到了客户端发来的数据: " + resultData; write(asc, response); } @Override public void failed(Throwable exc, ByteBuffer attachment) { exc.printStackTrace(); } }); } private void write(AsynchronousSocketChannel asc, String response) { try { ByteBuffer buf = ByteBuffer.allocate(1024); buf.put(response.getBytes()); buf.flip(); asc.write(buf).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public void failed(Throwable exc, Server attachment) { exc.printStackTrace(); }}
客户端
package com.wpx.aio;import java.io.UnsupportedEncodingException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.util.concurrent.ExecutionException;public class Client implements Runnable{ private AsynchronousSocketChannel asc ; public Client() throws Exception { asc = AsynchronousSocketChannel.open(); } public void connect(){ asc.connect(new InetSocketAddress("127.0.0.1", 8888)); } public void write(String request){ try { asc.write(ByteBuffer.wrap(request.getBytes())).get(); read(); } catch (Exception e) { e.printStackTrace(); } } private void read() { ByteBuffer buf = ByteBuffer.allocate(1024); try { asc.read(buf).get(); buf.flip(); byte[] respByte = new byte[buf.remaining()]; buf.get(respByte); System.out.println(new String(respByte,"utf-8").trim()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @Override public void run() { while(true){ } } public static void main(String[] args) throws Exception { Client c1 = new Client(); c1.connect(); Client c2 = new Client(); c2.connect(); Client c3 = new Client(); c3.connect(); new Thread(c1, "c1").start(); new Thread(c2, "c2").start(); new Thread(c3, "c3").start(); Thread.sleep(1000); c1.write("c1 aaa"); c2.write("c2 bbbb"); c3.write("c3 ccccc"); }}
阅读全文
0 0
- NIO的前世今生
- 前世今生的缘
- 互联网的前世今生
- 救世主的前世今生
- 3G的前世今生
- Gentoo的前世今生
- Mozilla的前世今生
- Java的“前世今生”
- unix的前世今生
- 浏览器的前世今生
- JAVA的前世今生
- bug的前世今生
- CKO的前世今生
- BIM的前世今生
- XVID的前世今生
- ActionScript的前世今生
- Android的前世今生
- Foursquare的前世今生
- 智能TV开发笔记(一)
- Reinforcement Learning——DP
- 为什么要使用React
- 清晰明了搞懂 call、apply、bind 的区别
- 创建包含多个子网的虚拟网络
- NIO的前世今生
- 练习(二)
- java蓝桥杯:外星日历
- css input checkbox和radio样式美化
- visualvm 远程监控Linux下的tomcat
- 对向量求导
- Ubuntu14.04安装Ubuntu16.04版的网易云音乐
- Rxbinding防抖动
- 字符串的格式化