5.Java AIO编程

NIO

跳转到NIO
-不是真异步,是一个同步的通讯模式

举例:假如你去饭店吃饭

  • 同步阻塞:当你下完单,你只能等在饭店里面,啥事也不能做,等待厨师完成,并亲自和饭店完成交接。
  • 同步非阻塞:你下完单以后,可以外出,不用一直等待。但是会采用定期轮询的方法,随时来看饭菜是否完成。如果已制作完成,也需要亲自和饭店完成交接。
  • 异步非阻塞:下完单后,可以外出,也不要定期查看厨师是否做好了(不用定期轮询)。而是交代下来,制作完成后,自动送到家里。(即制作完成后,自动进行一个回调函数执行(自动送达操作))

并发编程的同步和网络通讯的同步两者的区别:

  1. 并发编程同步:是指多个线程需要以一种同步的方式来访问一个数据结构。这里同步的反义词是非同步的,即线程不安全的。
  2. 网络编程同步:是指客户端和服务端直接的通讯等待方式。这里的同步的反义词是异步,即无需等待另一端操作完成。

AIO

  • Asynchronous I/O,异步I/O
  • JDK1.7引入,主要在java.nio包中
  • 异步I/O,采用 回调方法 进行处理读写操作
  • 主要类:
    • AsynchronousServerSocketChannel 服务器接受客户端请求通道
      • bind绑定在某一个端口 accept 接受客户端请求
    • AsynchronousSocketChannel Socket通讯通道
      • read 读数据 write 写数据
  • CompletionHandler 异步处理类
    • completed 操作完成后异步调用方法 failed 操作失败后异步调用方法
    • 每一个匿名的CompletionHandler 对象都要实现一个failed方法

示例:
先运行服务器端程序

package aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AioServer {

    public static void main(String[] args) throws IOException {  
    	AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();   
        server.bind(new InetSocketAddress("localhost", 8001));  
        System.out.println("服务器在8001端口守候");
        
        //开始等待客户端连接,一旦有连接,做26行任务
        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {  
            @Override  //complete方法是指连接被服务器接收以后,当通道建立好以后,应该做什么
            public void completed(AsynchronousSocketChannel channel, Object attachment) {  
            	 server.accept(null, this); //持续接收新的客户端请求
            	 
                 ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间
                 //开始读取客户端内容,一旦读取结束,做33行任务
                 channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                     @Override    //此complete方法,是指通道里面读取数据,读取完毕以后应该做什么
                     public void completed(Integer result_num, ByteBuffer attachment) {
                         attachment.flip(); //反转此Buffer 
                         CharBuffer charBuffer = CharBuffer.allocate(1024);
                         CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
                         decoder.decode(attachment,charBuffer,false);
                         charBuffer.flip();
                         String data = new String(charBuffer.array(),0, charBuffer.limit());
                         System.out.println("client said: " + data);
                         channel.write(ByteBuffer.wrap((data + " 666").getBytes())); //返回结果给客户端
                         try{
                             channel.close();
                         }catch (Exception e){
                        	 e.printStackTrace();
                         }
                     }
      
                     @Override
                     public void failed(Throwable exc, ByteBuffer attachment) {
                         System.out.println("read error "+exc.getMessage());
                     }
                 });
                 

            }  
  
            @Override  
            public void failed(Throwable exc, Object attachment) {  
                System.out.print("failed: " + exc.getMessage());  
            }  
        });  

        while(true){
        	try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
    }  
}

在这里插入图片描述

在运行客户端程序

package aio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.UUID;


public class AioClient {

	public static void main(String[] a) {
		try
		{   //用于连接到服务器的通道
			AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
			
			//18行连接成功后,自动做20行任务
			channel.connect(new InetSocketAddress("localhost", 8001), null, new CompletionHandler<Void, Void>() {
                //此complete方法是指connect成功调用以后才会执行
				public void completed(Void result, Void attachment) {
					String str = UUID.randomUUID().toString();  //创建一个随机字符串
					
					//24行向服务器写数据成功后,自动做28行任务
					channel.write(ByteBuffer.wrap(str.getBytes()), null,
							new CompletionHandler<Integer, Object>() {

								@Override     //此complete方法,在write成功以后才会执行
								public void completed(Integer result, Object attachment) {
									try {
										System.out.println("write " + str + ", and wait response");
										//等待服务器响应
										ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间
						                 //开始读取服务器反馈内容,一旦读取结束,做39行任务
										channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
						                     @Override  //此complete方法,是在read 成功以后才会执行
						                     public void completed(Integer result_num, ByteBuffer attachment) {
						                         attachment.flip(); //反转此Buffer 
						                         CharBuffer charBuffer = CharBuffer.allocate(1024);
						                         CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
						                         decoder.decode(attachment,charBuffer,false); 
						                         charBuffer.flip();
						                         String data = new String(charBuffer.array(),0, charBuffer.limit());
						                         System.out.println("server said: " + data);
						                         try{
						                             channel.close();
						                         }catch (Exception e){
						                        	 e.printStackTrace();
						                         }
						                     }
						      
						                     @Override
						                     public void failed(Throwable exc, ByteBuffer attachment) {
						                         System.out.println("read error "+exc.getMessage());
						                     }
						                 });
						                 
										channel.close();
									} catch (Exception e) {
										e.printStackTrace();
									}
								}

								@Override
								public void failed(Throwable exc, Object attachment) {
									System.out.println("write error");
								}

							});
				}

				public void failed(Throwable exc, Void attachment) {
					System.out.println("fail");
				}

			});
			Thread.sleep(10000);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述Server 收到客户端Clientfa过来一个随机字符串

在这里插入图片描述
服务端Server 回复了一个666的附加内容给客户端。客户端向Server输出一个随机字符串,收到Server返回的内容为原来随机字符串加上一个666。
以上程序和步骤就代表完成了一次客户端和服务端之间的AIO的交互

注意:程序中用到的回调方法,每一个操作都可以设置它的回调方法

channel.connect(new InetSocketAddress("localhost", 8001), null, new CompletionHandler<Void, Void>() {
                //此complete方法是指connect成功调用以后才会执行
				public void completed(Void result, Void attachment) 

如上代码块,当connect成功了,我就设置一个回调方法。一旦connect成功就执行

public void completed(Void result, Void attachment)

方法。以前写代码我们手动(如用if…else)去判断connect是否成功。现在可以采用异步的方式,直接把要处理的代码,直接放在connect里面,当作回调参数放进来,什么时候connect成功,什么时候执行这个completed方法。
在这里插入图片描述
connect什么时候结束我不知道,但是,我给它了一个指令,connect成功以后,就执行24行的completed方法( 这里好比前面举的例子,你在饭店里下了一个单,你外出了,厨师什么时候弄好,你不用知道,厨师弄好了以后能通知到你,并且把饭送到你手上。)。同理其他的调函数也一样。
回调函数写起来很方便,但若嵌套很多,代码阅读和维护会变得比较困难。

三种I/O的区别:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值