• JAVA中IO流模型BIO,NIO,AIO


    一。BIO,NIO,AIO简介

    要弄懂这些io模型必须弄懂以下这些概念
    同步:的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 比如火车站出站口等待来深圳过年的父母 只有接到父母才能做其他事情 等待过程 不停的电话父母 火车是否到站 是否出站 直到接到父母

    异步:指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知) 告诉朋友,朋友去接父母 自己忙自己的 朋友接到后 电话通知。(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS) 

    阻塞:所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 
    直到有东西可读或者可写为止
    非阻塞:非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待,

    读懂上面几个概念后就可以引入io的模型了

    同步阻塞IO(JAVA BIO): 
        同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 

    同步非阻塞IO(Java NIO) : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。 

    (Java AIO(NIO.2))异步非阻塞IO:  
       在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。    

    二。BIO,NIO,AIO编程实现

    模拟场景  服务器接受到消息后 发回一个消息  

    1.BIO编程实现

    BIO是同步阻塞IO流 每个获取的连接都需要创建一个新的处理线程


    服务器端:

    package cn.et;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 同步阻塞模型BIO
     * 使用byte[]数组缓冲数据
     * @author jiaozi
     *
     */
    public class BIOServer {
    
    	private static ServerSocket serverSocket=null;
    	private static int bufferSize=4096;
    	private static byte[] readBuffer=new byte[bufferSize];
    
    	public static void main(String[] args) throws IOException {
    		serverSocket = new ServerSocket(8899);
    		//这里表示同步 必須等待接受到一个Socket才能执行后面的代码
    		while(true) {
    			//獲取一個客戶端連接
    			Socket accept = serverSocket.accept();
    			//必須為一個客戶端連接創建一個處理線程
    			DThread th=new DThread(accept);
    			th.start();
    		}
    		
    	}
    	/**
    	 * 處理每個連接的線程 主要讀和寫操作
    	 * @author jiaozi
    	 */
    	static class DThread extends Thread{
    		Socket accept;
    		public DThread(Socket accept) {
    			this.accept = accept;
    		}
    
    		public void run() {
    			//獲取輸入流
    			try {
    				InputStream inputStream = this.accept.getInputStream();
    				OutputStream outputStream=this.accept.getOutputStream();
    				//沒有數據讀到 會阻塞直到有數據
    				int readCount = inputStream.read(readBuffer);
    				String readText=new String(readBuffer,0,readCount);
    				System.out.println("讀取的數據為:"+readText);
    				outputStream.write("hello client".getBytes());
    				outputStream.flush();
    				accept.close();
    				
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    客户端:

    package cn.et;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    /**
     * BIO的客戶端
     * @author jiaozi
     */
    public class BIOClient {
    	private static int port=8899;
    	private static int bufferSize=4096;
    	private static byte[] readBuffer=new byte[bufferSize];
    	public static void main(String[] args) throws UnknownHostException, IOException {
    		Socket socket=new Socket("localhost",port);
    		OutputStream outputStream = socket.getOutputStream();
    		InputStream inputStream = socket.getInputStream();
    		outputStream.write("hello server".getBytes());
    		outputStream.flush();
    		int readCount = inputStream.read(readBuffer);
    		String readText=new String(readBuffer,0,readCount);
    		System.out.println("讀取的數據為:"+readText);
    	}
    }
    
    演示 结果 启动BIOServer 发现BIOServer处于同步阻塞状态  启动客户端 
    服务端接受到消息 :讀取的數據為:hello server
    客户端接受到消息  : 讀取的數據為:hello client
    2.NIO编程实现
    Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。
    下面是java NIO的工作原理:
     由一个专门的线程来处理所有的 IO 事件,并负责分发。
     事件驱动机制:事件到的时候触发,而不是同步的去监视事件。

     线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

    NIO的原理
    服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止

    核心对象
    Buffer和Channel是标准NIO中的核心对象
       Buffer:是一个对象,它包含一些要写入或读出的数据。在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的。
    使用 Buffer 读写数据一般遵循以下四个步骤:
    写入数据到 Buffer;
    调用 flip() 方法;
    从 Buffer 中读取数据;
    调用 clear() 方法或者 compact() 方法。
       Channel:是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:
    Channel是双向的,既可以读又可以写,而流是单向的
    Channel可以进行异步的读写
    对Channel的读写必须通过buffer对象
    在Java NIO中Channel主要有如下几种类型:
     FileChannel:从文件读取数据的
     DatagramChannel:读写UDP网络协议数据
     SocketChannel:读写TCP网络协议数据
     ServerSocketChannel:可以监听TCP连接

    服务器端:

    package cn.et;
    
    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;
    import java.util.Set;
    
    /**
     * NIO 同步非阻塞 該種方式 阻塞多路複用器 Selector 使用轮询方式查找是否存在io事件
     *   IO事件分为  OP_WRITE[缓冲区可以写入],OP_READ【缓存区有数据可读】,OP_CONNECT【客户端连接】,OP_ACCEPT[服务端接受到一个连接]
     * @author jiaozi
     */
    public class NIOServer {
    	
    	private static ServerSocketChannel serverSocketChannel=null;
    	private static int bufferSize=4096;
    	private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);
    	private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);
    	private static Selector selector;
    	
    	public static void main(String[] args) throws IOException {
    		//打开通道
    		serverSocketChannel=ServerSocketChannel.open();
    		//设置为非阻塞
    		serverSocketChannel.configureBlocking(false);
    		serverSocketChannel.bind(new InetSocketAddress(8899));
    		selector=Selector.open();
    		//通道注册到多路复用器 selector中 selector监听事件 接受客户端请求事件
    		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    		listen();
    	}
    	public static void listen() throws IOException {
    		//存在注册的事件返回时 方法返回 否则一直阻塞 应该轮询
    		while(true) {
    			selector.select();
    			//select到了后面 说明存在事件 获取事件 并轮询
    			Set<SelectionKey> selectedKeys = selector.selectedKeys();
    			Iterator<SelectionKey> iterator = selectedKeys.iterator();
    			while(iterator.hasNext()) {
    				SelectionKey selectionKey = iterator.next();
    				iterator.remove();
    				//有客户端连接事件
    				if(selectionKey.isAcceptable()) {
    					  ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();  
    	                  // 获得和客户端连接的通道  
    	                  SocketChannel channel = server.accept(); 
    	                  //这里要配置一下 否则报错
    	                  channel.configureBlocking(false);
    	                  //给接入的客户端注册读的事件
    	                  channel.register(selector, SelectionKey.OP_READ);
    	                  writeBuffer.put("hello client".getBytes());
    	                  writeBuffer.flip();
    	                  //写入测试数据
    	                  channel.write(writeBuffer);
    				}
    				//缓冲区有数据可读事件
    				if(selectionKey.isReadable()) {
    					SocketChannel channel = (SocketChannel) selectionKey.channel();  
    					int readCount=channel.read(readBuffer);
    					String readText=new String(readBuffer.array(),0,readCount);
    					System.out.println("讀取的數據為:"+readText);
    					readBuffer.clear();
    				}
    			}
    		}
    	}
    }
    

    客户端:

    package cn.et;
    
    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.SocketChannel;
    import java.util.Iterator;
    
    public class NIOClient {
    	private static SocketChannel socketChannel=null;
    	private static int bufferSize=4096;
    	private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);
    	private static Selector selector;
    	private static int port=8899;
    	public static void main(String[] args) throws IOException {
    		socketChannel=SocketChannel.open();
    		selector=Selector.open();
    		socketChannel.configureBlocking(false);
    		socketChannel.register(selector, SelectionKey.OP_CONNECT);
    		socketChannel.connect(new InetSocketAddress(port));
    		connect();
    	}
    	public static void connect() throws IOException {
    		// 轮询访问selector    
            while (true) {    
                // 选择一组可以进行I/O操作的事件,放在selector中,客户端的该方法不会阻塞,    
                //这里和服务端的方法不一样,查看api注释可以知道,当至少一个通道被选中时,    
                //selector的wakeup方法被调用,方法返回,而对于客户端来说,通道一直是被选中的    
                selector.select();    
                // 获得selector中选中的项的迭代器    
                Iterator<SelectionKey> ite = selector.selectedKeys().iterator();    
                while (ite.hasNext()) { 
                	SelectionKey selectionKey = ite.next();
                	ite.remove();
                	//如果是连接事件
                	if(selectionKey.isConnectable()) {
                		SocketChannel channel = (SocketChannel) selectionKey.channel();    
                		//如果正在连接 等待完成连接
                		if(channel.isConnectionPending()) {
                			channel.finishConnect();
                		}
                		channel.write(ByteBuffer.wrap(new String("hello server").getBytes()));    
                        //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。    
                        channel.register(selector, SelectionKey.OP_READ);   
                	}
                	if(selectionKey.isReadable()) {
                		SocketChannel channel = (SocketChannel) selectionKey.channel();  
    					int readCount=channel.read(readBuffer);
    					String readText=new String(readBuffer.array(),0,readCount);
    					System.out.println("讀取的數據為:"+readText);
    					readBuffer.clear();
                	}
                }
            }
    	}
    
    }
    
    3.AIO编程实现

    aio是实现了真正的异步非阻塞 将所有的io将给操作系统处理 当io事件触发时 回调用户的Handler即可 不再有nio1的轮询 linux下epol也是基于aio  aio是在jdk1.7开始实现的

    服务端:

    package cn.et;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.AsynchronousServerSocketChannel;
    import java.nio.channels.AsynchronousSocketChannel;
    import java.nio.channels.CompletionHandler;
    
    public class AIOServer {
    	private static AsynchronousServerSocketChannel serverSocketChannel=null;
    	private static int bufferSize=4096;
    	private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);
    	private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);
    	
    	public static void main(String[] args) throws IOException {
    		serverSocketChannel=AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8899));
    		serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>(){
    
    			@Override
    			public void completed(AsynchronousSocketChannel channel, Object attachment) {
    				try {
    					
    					//读的异步
    					channel.read(readBuffer, null, new CompletionHandler<Integer, Object>() {
    
    						@Override
    						public void completed(Integer result, Object attachment) {
    							String readText=new String(readBuffer.array(),0,result);
    							System.out.println("讀取的數據為:"+readText);
    							readBuffer.clear();
    						}
    
    						@Override
    						public void failed(Throwable exc, Object attachment) {
    							exc.printStackTrace();
    							System.out.println("读取失败");
    						}
    						
    					});
    					writeBuffer.put("hello client".getBytes());
    	                writeBuffer.flip();
    	                //写入测试数据
    	                channel.write(writeBuffer);
    	                serverSocketChannel.accept(null, this);// 监听新的请求,递归调用。
    				} catch (Exception e) {
    					e.printStackTrace();
    				} 
    			}
    
    			@Override
    			public void failed(Throwable exc, Object attachment) {
    				System.out.println("出现异常:"+exc);
    				serverSocketChannel.accept(null, this);// 监听新的请求,递归调用。
    			}
    			
    		});
    		
    				
    		System.in.read();
    	}
    
    }
    
    客户端:
    package cn.et;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.AsynchronousSocketChannel;
    import java.nio.channels.CompletionHandler;
    import java.nio.channels.Selector;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    public class AIOClient {
    	private static AsynchronousSocketChannel socketChannel=null;
    	private static int bufferSize=4096;
    	private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);
    	private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);
    	private static Selector selector;
    	private static int port=8899;
    	public static void main(String[] args) throws Exception {
    		socketChannel=AsynchronousSocketChannel.open();
    		//这里一定要指定ip 就算是本机  也要使用localhost
    		socketChannel.connect(new InetSocketAddress("localhost",port),null,new CompletionHandler<Void, Object>() {
    
    			@Override
    			public void completed(Void result, Object attachment) {
    				try {
    					//读的异步
    					socketChannel.read(readBuffer, null, new CompletionHandler<Integer, Object>() {
                       
    						@Override
    						public void completed(Integer result, Object attachment) {
    							String readText=new String(readBuffer.array(),0,result);
    							System.out.println("讀取的數據為:"+readText);
    						}
    
    						@Override
    						public void failed(Throwable exc, Object attachment) {
    							exc.printStackTrace();
    							System.out.println("读取失败");
    						}
    						
    					});
    					//写入数据到服务端
    					writeBuffer.put("hello server".getBytes());
    					writeBuffer.flip();
    					//写入测试数据
    					socketChannel.write(writeBuffer);
    					
    				} catch (Exception e) {
    					e.printStackTrace();
    				} 
    			}
    
    			@Override
    			public void failed(Throwable exc, Object attachment) {
    				// TODO Auto-generated method stub
    				
    			}
    		});
    		
    		
    		System.in.read();
            
    	}
    }
    





  • 相关阅读:
    FileUpload的使用
    关于hibernate4的配置我要好好反省一下
    比较SQL Server 2000 数据库中两个库的差异
    用google生活
    用OWC11图形分析本页面及其他页面Table中的数据
    请教ASP.NET培训应该培训的内容和以及顺序
    最近一个快要结束的项目的BUG分析
    我也发软件开发团队的思考(侧重点是人员)
    一个SQL语句的问题,我百思不得其解,请教各位
    分享C#高端视频教程
  • 原文地址:https://www.cnblogs.com/liaomin416100569/p/9331142.html
Copyright © 2020-2023  润新知