JAVA AIO NIO BIO笔记

版权声明:如果是原创,请联系作者进行转载 https://blog.csdn.net/u013159507/article/details/84379161

说道实现原理,还要从操作系统的IO模型上了解

按照《Unix网络编程》的划分,IO模型可以分为:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO。如何区分呢?首先一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作,同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO,如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

可以理解的说明是:在Linux 2.6以后,java NIO的实现,是通过epoll来实现的,这点可以通过jdk的源代码发现。而AIO,在windows上是通过IOCP实现的,在linux上通过新的API来实现。

编程语言层面的理解

 以Java的角度,理解如下:

  • BIO,同步阻塞式IO,简单理解:一个线程处理一个连接,发起和处理IO请求都是同步的
  • NIO,同步非阻塞IO,简单理解:一个线程处理多个连接,发起IO请求是非阻塞的但处理IO请求是同步的
  • AIO,异步非阻塞IO,简单理解:一个有效请求一个线程,发起和处理IO请求都是异步的

BIO:

在JDK1.4之前,用Java编写网络请求,都是建立一个ServerSocket,然后,客户端建立Socket时就会询问是否有线程可以处理,如果没有,要么等待,要么被拒绝。即:一个连接,要求Server对应一个处理线程。

NIO:

在Java里的由来,在JDK1.4及以后版本中提供了一套API来专门操作非阻塞I/O,我们可以在java.nio包及其子包中找到相关的类和接口。由于这套API是JDK新提供的I/O API,因此,也叫New I/O,这就是包名nio的由来。这套API由三个主要的部分组成:缓冲区(Buffers)、通道(Channels)和非阻塞I/O的核心类组成。在理解NIO的时候,需要区分,说的是New I/O还是非阻塞IO,New I/O是Java的包,NIO是非阻塞IO概念。这里讲的是后面一种。

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。

NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。 
也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

NIO客户端代码:

mport 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;
import java.util.Set;

/**
 * 实现服务端与客户端的简单通信
 */
public class NIOClient {

    private  static  int flag = 0;
    private static   int blockSize = 4096;

    //发送数据的缓冲区
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);
    //接受数据的缓冲区
    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(blockSize);
    private final static InetSocketAddress serverAddress = new InetSocketAddress("localhost",8080);


    public static  void main(String []args) throws IOException {
        SocketChannel  socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        //打开选择器
        Selector selector = Selector.open();
        socketChannel.register(selector,SelectionKey.OP_CONNECT);
        socketChannel.connect(serverAddress);

        Set<SelectionKey> selectionKeys ;

        Iterator<SelectionKey> iterator;
        SelectionKey selectionKey;
        SocketChannel client;
        String receivText;
        String sendText;
        int count = 0;

        while (true){
            //选择一组建,其相应的通道已经为I/O操作准备就绪
            selector.select();
           selectionKeys =  selector.selectedKeys();
           iterator = selectionKeys.iterator();
           while (iterator.hasNext()){
               selectionKey = iterator.next();
               if (selectionKey.isConnectable()){
                   System.out.println("client is connecting");
                   client = (SocketChannel) selectionKey.channel();
                   if (client.isConnectionPending()){
                       client.finishConnect();
                       System.out.println("客户端完成连接操作");
                       sendBuffer.clear();

                       sendBuffer.put("你好,Server....".getBytes());
                       sendBuffer.flip();
                       client.write(sendBuffer);

                   }
                   client.register(selector,SelectionKey.OP_READ);
               }else  if(selectionKey.isReadable()){
                   client =  (SocketChannel) selectionKey.channel();
                   receiveBuffer.clear();
                   count = client.read(receiveBuffer);
                   if (count>0){
                       receivText = new String(receiveBuffer.array(),0,count);
                       System.out.println("客户端接收到服务端数据:"+receivText);
                       client.register(selector,SelectionKey.OP_WRITE);
                   }
               }else if (selectionKey.isWritable()){
                   sendBuffer.clear();
                   client = (SocketChannel) selectionKey.channel();
                   sendText = "Msg send to server->"+flag++;
                   sendBuffer.put(sendText.getBytes());
                   sendBuffer.flip();
                   client.write(sendBuffer);
                   System.out.println("客户端发送数据给服务端:"+sendText);
                   client.register(selector,SelectionKey.OP_READ);
               }
           }

           selectionKeys.clear();
        }
    }
}

NIO服务端代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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;

public class NIOServer {

    private int flag = 1;
    private int blockSize = 4096;

    //发送数据的缓冲区
    private ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);
    //接受数据的缓冲区
    private ByteBuffer receiveBuffer = ByteBuffer.allocate(blockSize);
    //
    private Selector selector ;

    public NIOServer(int port) throws IOException {
        //打开服务端的套接字通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //非阻塞通信(设置是否阻塞)
        serverSocketChannel.configureBlocking(false);

        //检索与此通道关联的服务器套接字
        ServerSocket serverSocket = serverSocketChannel.socket();
        //绑定IP和端口
        serverSocket.bind(new InetSocketAddress(port));

        //打开选择器
        selector = Selector.open();

        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
        System.out.println("Server Start Listening  at  port :"+port);
    }


    //监听事件
    public  void listen() throws IOException {
        while (true){
            //选择一组建,并且相应的通道已经打开
            selector.select();
            Set<SelectionKey> selectionKeySet =  selector.selectedKeys();
            Iterator<SelectionKey> iterable = selectionKeySet.iterator();
            while (iterable.hasNext()){

                SelectionKey selectionKey =  iterable.next();
                iterable.remove();

                //业务逻辑
                handleKey(selectionKey);
            }
        }
    }

    public void handleKey(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel server = null;
        SocketChannel client = null;
        String receviText;
        String sendText;
        int count = 0;
        if (selectionKey.isAcceptable()){
            //返回之前创建此建的通道
            server = (ServerSocketChannel) selectionKey.channel();
            client = server.accept();
            client.configureBlocking(false);
            client.register(selector,SelectionKey.OP_READ);
        }else if (selectionKey.isReadable()) {
            //获取之前创建的客户端通道
            client = (SocketChannel)selectionKey.channel();
            //服务器端的接受缓冲区清空
            receiveBuffer.clear();
            //把客户端的数据放到服务端的接受缓冲区,并获取数据缓冲区大小赋予count
            count = client.read(receiveBuffer);
            if (count >0) {
                receviText = new String(receiveBuffer.array(),0,count);
                System.out.println("服务端接收到客户端的信息:"+receviText);
                client.register(selector,SelectionKey.OP_WRITE);
            }
        }else if(selectionKey.isWritable()){
            //发送缓冲区清空
            sendBuffer.clear();
            //获取客户端通道
            client  = (SocketChannel)selectionKey.channel();

            sendText = "msg send to client:"+flag++;
            //发送数据缓冲区存放需要发送的数据
            sendBuffer.put(sendText.getBytes());

            //将缓冲区个标识位复位,因为向缓冲区里面放数据,如果需要把数据发送给服务器,就要复位
            sendBuffer.flip();

            client.write(sendBuffer);
            System.out.println(" Server 已经发送数据给客户端:"+sendText);
            client.register(selector,SelectionKey.OP_READ);
        }

    }

    public static  void main(String []args) throws IOException {
        int port  = 8080;
        NIOServer nioServer  = new NIOServer(port);
        nioServer.listen();
    }
}

AIO:

与NIO不同,操作系统负责处理内核区/用户区的内存数据迁移和真正的IO操作,应用程序只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 
即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 
在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:

  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousFileChannel
  • AsynchronousDatagramChannel

其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

AIO服务端代码:

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;
import java.util.concurrent.ExecutionException;

public class AIOServer {

    public AIOServer(int port) throws IOException {
        AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open()
                .bind(new InetSocketAddress(port));
        listener.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Void vi) {
                //接受下一个链接
                listener.accept(null,this);

                    handle(result);
            }

            @Override
            public void failed(Throwable exc, Void vi) {
                System.out.println("异步IO失败");
            }
        });
    }

    //真正逻辑
    public void  handle(AsynchronousSocketChannel ch) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(32);
        try {
            ch.read(byteBuffer).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        byteBuffer.flip();
        System.out.println("服务端接收:"+byteBuffer.get());
    }

    public static  void main(String args[]) throws Exception {
        AIOServer server = new AIOServer(7080);
        System.out.println("服务监听端口:"+7080);
        Thread.sleep(10000);

    }
}

AIO客户端代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class AIOClient {

    private AsynchronousSocketChannel client = null;
    AIOClient(String host,int port) throws IOException, ExecutionException, InterruptedException {
        client = AsynchronousSocketChannel.open();
        Future<?> future =  client.connect(new InetSocketAddress(host,port));
        System.out.println( future.get());
    }

    public void write(byte b){
        ByteBuffer byteBuffer = ByteBuffer.allocate(32);
        byteBuffer.put(b);
        byteBuffer.flip();
        client.write(byteBuffer);
    }

    public static  void main(String args[]) throws InterruptedException, ExecutionException, IOException {
        AIOClient client = new AIOClient("localhost",7080);
        client.write((byte) 29);
    }
}

猜你喜欢

转载自blog.csdn.net/u013159507/article/details/84379161
今日推荐