NIO, AIO, BIO는 무엇입니까?

I. 서론

NIO

동기 비 블록 I / O.

AIO

비동기 I / O를 비 차단

BIO

동기 차단 IO 작업.

둘째, 용어

차단 및 비 차단

스레드가 차단 작업을 실행할 때, 우리는 기다릴 수 있지만, 다른 일을 수행 할 수 없습니다.
비 블로킹 게임은 기다릴 필요, 직접 반환, 다음 작업을 계속하지 않습니다.

동기 및 비동기

동기 비동기 작업 메커니즘은 우리가 동기화 할 때, 후속 작업이 현재를 기다리는 것은, 돌아 호출하는 다음 단계가 될 것입니다.
비동기 대비, 현재를 기다리지 않고 다른 작업은 종종 작업 간 관계의 순서를 구현하는 콜백 메커니즘으로, 이벤트에 의존 돌아 호출합니다.

IO 분류

바이트 스트림 (리더 라이터)와 문자 스트림 (의 InputStream, OutputStream에) 누름 조작 데이터로 분할되는
흐름 포인트 : 입력 스트림 (리더의 InputStream) 및 출력 스트림 (작가의 OutputStream)

셋째, 실제 데모

BIO

서버 코드 :

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.bio
 * @ClassName: BIOServer
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 14:30
 * @Version: 1.0
 */
public class BIOServer {

    ServerSocket server;
    //服务器
    public BIOServer(int port){
        try {
            //把Socket服务端启动
            server = new ServerSocket(port);
            System.out.println("BIO服务已启动,监听端口是:" + port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始监听,并处理逻辑
     * @throws IOException
     */
    public void listener() throws IOException{
        //死循环监听
        while(true){
            //等待客户端连接,阻塞方法
            Socket client = server.accept();
            //获取输入流
            InputStream is = client.getInputStream();
            //定义数组,接收字节流
            byte [] buff = new byte[1024];
            int len = is.read(buff);
            //只要一直有数据写入,len就会一直大于0
            if(len > 0){
                String msg = new String(buff,0,len);
                System.out.println("收到" + msg);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new BIOServer(9999).listener();
    }
}

클라이언트 코드 :

import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.bio
 * @ClassName: BIOClient
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 14:31
 * @Version: 1.0
 */
public class BIOClient {

    public static void main(String[] args){
        int count = 10;
        //计数器,模拟10个线程
        final CountDownLatch latch = new CountDownLatch(count);
        for(int i = 0 ; i < count; i ++){
            new Thread(){
                @Override
                public void run() {
                    try{
                        //等待,保证线程一个一个创建连接
                        latch.await();
                        //创建socket,连接服务端
                        Socket client = new Socket("localhost", 9999);
                        //获取输出流
                        OutputStream os = client.getOutputStream();
                        //获取当前线程名
                        String name = "客户端线程:"+Thread.currentThread().getName();
                        //发送到服务端
                        os.write(name.getBytes());
                        //关闭输入流
                        os.close();
                        //关闭socket连接
                        client.close();

                    }catch(Exception e){

                    }
                }

            }.start();
            //计数器减1
            latch.countDown();
        }
    }
}

결과 :

收到客户端线程:Thread-1
收到客户端线程:Thread-4
收到客户端线程:Thread-0
收到客户端线程:Thread-3
收到客户端线程:Thread-2
收到客户端线程:Thread-6
收到客户端线程:Thread-8
收到客户端线程:Thread-7
收到客户端线程:Thread-5
收到客户端线程:Thread-9

는 설명 할 간단한 다음, 서버를 시작한 후, 서버는 클라이언트가 연결을 위해 클라이언트에서 보낸 정보를 읽어 진행하기 전에, 대기, 대기 상태에서 차단 된 것, 다음 새를 처리 할 수있는 새로운 접속을 대기 루프 폐쇄를 입력 정보 데이터. 위, 우리는 10 개 스레드 클라이언트가 정보를주기에서 서버, 길을 차단 클라이언트 정보를 통해 패스를 받고를 보내도록 서버를 연결 시뮬레이션 만듭니다.

NIO

NIO 세 가지 주요 구성 요소 :

채널 (채널), 버퍼 (완충), 선택기 (선택)

NIO 실행 과정 :

클라이언트 서버 NIO 연결을 사용하는 경우, 첫 번째는 데이터 버퍼 (버퍼)에 기록되는 채널 (채널)와 채널 선택기를 등록 마지막 이벤트를 통해 전송 버퍼 데이터를 열 것이다.
서버는 첫 번째 채널로부터의 버퍼를 얻기 위해이 발견되면 채널 (채널), 이벤트 후에, 이벤트의 선택을 수신 열고 버퍼로부터 데이터를 판독한다. 그렇지 않은 경우,주기는 듣고있다.

채널 (채널)

채널 데이터를 읽고 쓸 수있는 객체입니다.
양방향 채널 모두 읽고 쓸 수 있습니다.
채널 비동기 적으로 읽을 수 있습니다.
채널은 읽기와 쓰기 버퍼 오브젝트를 통과해야합니다.

주로 다음과 같은 유형의 채널 자바 NIO :
되는 FileChannel : 파일에서 데이터를 읽어
DatagramChannel 데이터 : UDP 네트워크 프로토콜 것은 읽기 및 데이터 쓰기
에 SocketChannel을 : TCP 네트워크 프로토콜 데이터 읽기
의 ServerSocketChannel을 : TCP 연결을 모니터링 할 수 있습니다

완충액 (버퍼)

버퍼는 숫자에 기록 또는 스트림 개체를 읽을 수 포함하는 객체입니다. 직접 판독 및 기록 동작을 할 수없는 채널 애플리케이션이 버퍼에 의해 수행되어야한다, 즉 채널은 판독 버퍼를 통해 데이터를 기록하는 것이다.

NIO에서 모든 데이터를 읽고 데이터 NIO 풀을 전송 쓰기되고 버퍼로 처리됩니다. 버퍼는 본질적으로 배열 된 데이터의 바이트 일반적이지만, 또한 다른 유형의 어레이 일 수있다. 그러나, 버퍼가 판독 프로세스 추적 시스템을 작성할 수 또한 데이터를 구조화 된 액세스를 제공하고, 단지 어레이 중요한 것은 아니지만.

버퍼 속성 설명 :

각 데이터 요소들의 최대 개수는 상기 버퍼에 수용 될 수있다 : 용량 (용량). 이 용량은 버퍼 영역 생성에 설정되고, 변경 될 수 없다.
상한 (제한) : 첫 번째 읽기 또는 요소 버퍼를 쓸 수 없습니다. 또는 버퍼 요소에 기존의 수입니다.
위치 (위치) : 다음 인덱스를 읽거나 요소를 작성하는 것입니다. 위치는 각각의 get () 넣어 () 함수에 의해 자동으로 업데이트됩니다.
마크 (마크) : 다음 인덱스 요소를 읽거나 쓸 수있다. 위치는 각각의 get () 넣어 () 함수에 의해 자동으로 업데이트됩니다.

읽기 및 데이터 단계를 작성 :

1. 쓰기 데이터는 버퍼에,
2 플립 () 메서드 호출을;
3. 버퍼의 데이터;
4. 호출 클리어 () 메서드 또는 컴팩트 () 메소드를.

데이터가 버퍼에 기록 될 때, 버퍼 쓰기 데이터의 수를 기록합니다. 판독 될 데이터가 요구되면 () 메소드 버퍼 플립 모드 읽기 쓰기 모드로 전환. 읽기 모드에서, 버퍼에 기록 된 모든 데이터는 이전에 읽어 보시기 바랍니다.

모든 데이터를 읽고 나면, 우리는 다시 쓸 수, 버퍼를 비울 필요가있다. 이 버퍼를 비울 수 있어야하는 두 가지 방법이 있습니다 : 투명 () 또는 소형 () 메서드를 호출합니다. 클리어 () 메소드는 전체 버퍼를 클리어. 데이터 만 읽어 컴팩트 () 메소드가 삭제되었습니다. 읽지 않은 데이터가 버퍼의 시작 부분으로 이동, 새로운 데이터는 다시 읽지 않은 데이터 버퍼에 기록됩니다.

몇 가지 주요 버퍼가 있습니다
ByteBuffer를
CharBuffer를
에 Doublebuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer

선택기 (선택)

여부 클라이언트 또는 서버가 이벤트에 선택기에 등록 할 수 있으며, 다음 이벤트를 수신하고, 마지막으로 이벤트에 따라 다르게 처리됩니다.

코드에서 다음보기
클라이언트 :

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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.nio
 * @ClassName: NIOClient
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 13:41
 * @Version: 1.0
 */
public class NIOClient {
    private final InetSocketAddress serverAdrress = new InetSocketAddress("localhost", 9999);
    private Selector selector = null;
    private SocketChannel client = null;
    private Charset charset = Charset.forName("UTF-8");

    public NIOClient() throws IOException{
        //1.连接远程主机的IP和端口
        client = SocketChannel.open(serverAdrress);
        client.configureBlocking(false);

        //打开选择器,注册读事件
        selector = Selector.open();
        client.register(selector, SelectionKey.OP_READ);
    }

    public void session(){
        //开辟一个新线程从服务器端读数据
        new Reader().start();
        //开辟一个新线程往服务器端写数据
        new Writer().start();
    }

    /**
     * 写数据线程
     */
    private class Writer extends Thread{

        @Override
        public void run() {
            try{
                //在主线程中 从键盘读取数据输入到服务器端
                Scanner scan = new Scanner(System.in);
                while(scan.hasNextLine()){
                    String line = scan.nextLine();
                    if("".equals(line)){
                        //不允许发空消息
                        continue;
                    }
                    //当前渠道是共用的,发送当前输入数据
                    client.write(charset.encode(line));
                }
                scan.close();
            }catch(Exception e){

            }
        }

    }

    /**
     * 读数据线程
     */
    private class Reader extends Thread {
        @Override
        public void run() {
            try {
                //循环检测
                while(true) {
                    int readyChannels = selector.select();
                    if(readyChannels == 0){
                        continue;
                    }
                    //获取selected所有的事件
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                    while(keyIterator.hasNext()) {
                        SelectionKey key = (SelectionKey) keyIterator.next();
                        keyIterator.remove();
                        process(key);
                    }
                }
            }
            catch (IOException io){

            }
        }

        /**
         * 根据事件的不同,做不同的处理
         * @param key
         * @throws IOException
         */
        private void process(SelectionKey key) throws IOException {
            //读就绪事件
            if(key.isReadable()){
                //通过key找到对应的通道
                SocketChannel sc = (SocketChannel)key.channel();
                //创建缓存区
                ByteBuffer buff = ByteBuffer.allocate(1024);
                String content = "";
                //读数据
                while(sc.read(buff) > 0){
                    buff.flip();
                    content += charset.decode(buff);
                }
                //打印内容
                System.out.println(content);
                //设置当前为读就绪
                key.interestOps(SelectionKey.OP_READ);
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        new NIOClient().session();
    }
}

서버 :

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.nio
 * @ClassName: NIOServer
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 13:23
 * @Version: 1.0
 */
public class NIOServer {

    private int port = 9999;
    private Charset charset = Charset.forName("UTF-8");
    private Selector selector = null;

    public NIOServer(int port) throws IOException{

        this.port = port;
        //1.打开通道
        ServerSocketChannel server = ServerSocketChannel.open();
        //设置服务端口
        server.bind(new InetSocketAddress(this.port));
        server.configureBlocking(false);
        //2.打开选择器
        selector = Selector.open();
        //注册等待事件
        server.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务已启动,监听端口是:" + this.port);
    }

    /**
     *  监听事件
     * @throws IOException
     */
    public void listener() throws IOException{
        //死循环,这里不会阻塞
        while(true) {
            //1.在轮询获取待处理的事件
            int wait = selector.select();
            System.out.println("当前等待处理的事件:"+wait+"个");
            if(wait == 0){
                //如果没有可处理的事件,则跳过
                continue;
            }
            //获取所有待处理的事件
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            //遍历
            while(iterator.hasNext()) {
                SelectionKey key = (SelectionKey) iterator.next();
                //处理前,关闭选在择器中的事件
                iterator.remove();
                //处理事件
                process(key);
                System.out.println("事件Readable:"+key.isReadable());
                System.out.println("事件Acceptable:"+key.isAcceptable());
            }
        }

    }

    /**
     * 根据事件类型,做处理
     * @param key
     * @throws IOException
     */
    public void process(SelectionKey key) throws IOException {
        //连接就绪
        if(key.isAcceptable()){
            //获取通道
            ServerSocketChannel server = (ServerSocketChannel)key.channel();
            //进入服务端等待
            SocketChannel client = server.accept();
            //非阻塞模式
            client.configureBlocking(false);
            //注册选择器,并设置为读取模式,收到一个连接请求,
            // 然后起一个SocketChannel,并注册到selector上,
            // 之后这个连接的数据,就由这个SocketChannel处理
            client.register(selector, SelectionKey.OP_READ);
            //将此对应的channel设置为准备接受其他客户端请求
            key.interestOps(SelectionKey.OP_ACCEPT);
            client.write(charset.encode("来自服务端的慰问"));
        }
        //读就绪
        if(key.isReadable()){
            //返回该SelectionKey对应的 Channel,其中有数据需要读取
            SocketChannel client = (SocketChannel)key.channel();

            //往缓冲区读数据
            ByteBuffer buff = ByteBuffer.allocate(1024);
            StringBuilder content = new StringBuilder();
            try{
                while(client.read(buff) > 0){
                    buff.flip();
                    content.append(charset.decode(buff));
                }
                System.out.println("接收到客户端:"+content.toString());
                //将此对应的channel设置为准备下一次接受数据
                key.interestOps(SelectionKey.OP_READ);
            }catch (IOException io){
                key.cancel();
                if(key.channel() != null){
                    key.channel().close();
                }
            }

        }
    }

    public static void main(String[] args) throws IOException {
        new NIOServer(9999).listener();
    }


}

결과 :

클라이언트 :

来自服务端的慰问
请输入要发送服务端的信息:
你好呀

서버 :

服务已启动,监听端口是:9999
当前等待处理的事件:1个
事件Readable:false
事件Acceptable:true
当前等待处理的事件:1个
接收到客户端:你好呀
事件Readable:true
事件Acceptable:false

다음은 위의 설명을 기초로 한 다음이 이해할 수있는 코드를 살펴. 의 전체 프로세스 ~

AIO

클라이언트 :

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;
/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.aio
 * @ClassName: AIOClient
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 14:03
 * @Version: 1.0
 */
public class AIOClient {

    private final AsynchronousSocketChannel client ;

    public AIOClient() throws Exception{

        client = AsynchronousSocketChannel.open();
    }

    public void connect(String host,int port)throws Exception{

        //连接服务端
        client.connect(new InetSocketAddress(host,port),null,new CompletionHandler<Void,Void>() {

            /**
             * 成功操作
             * @param result
             * @param attachment
             */
            @Override
            public void completed(Void result, Void attachment) {
                try {
                    client.write(ByteBuffer.wrap(("客户端线程:" + Thread.currentThread().getName()+"请求服务端").getBytes())).get();
                     System.out.println("已发送至服务器");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

            /**
             * 失败操作
             * @param exc
             * @param attachment
             */
            @Override
            public void failed(Throwable exc, Void attachment) {
                exc.printStackTrace();
            }
        });

        //读取数据
        final ByteBuffer bb = ByteBuffer.allocate(1024);
        client.read(bb, null, new CompletionHandler<Integer,Object>(){

                    /**
                     * 成功操作
                     * @param result
                     * @param attachment
                     */
                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("获取反馈结果:" + new String(bb.array()));
                    }

                    /**
                     * 失败操作
                     * @param exc
                     * @param attachment
                     */
                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        exc.printStackTrace();
                    }
                }
        );


        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }

    }

    public static void main(String args[])throws Exception{
        int count = 10;
        final CountDownLatch latch = new CountDownLatch(count);

        for (int i = 0; i < count; i ++) {
            latch.countDown();
            new Thread(){
                @Override
                public void run(){
                    try{
                        latch.await();
                        new AIOClient().connect("localhost",9999);
                    }catch(Exception e){

                    }
                }
            }.start();
        }

        Thread.sleep(1000 * 60 * 10);
    }


}

서버 :

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

import static java.util.concurrent.Executors.*;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.aio
 * @ClassName: AIOServer
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/9/1 14:04
 * @Version: 1.0
 */
public class AIOServer {


    private final int port;

    public static void main(String args[]) {
        int port = 9999;
        new AIOServer(port);
    }

    /**
     * 注册一个端口,用来给客户端连接
     * @param port
     */
    public AIOServer(int port) {
        this.port = port;
        listen();
    }

    //侦听方法
    private void listen() {
        try {
            //线程缓冲池,为了体现异步
            ExecutorService executorService = newCachedThreadPool();
            //给线程池初始化一个线程
            AsynchronousChannelGroup threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);

            //Asynchronous异步
            final AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(threadGroup);

            //启动监听
            server.bind(new InetSocketAddress(port));
            System.out.println("服务已启动,监听端口" + port);

            final Map<String,Integer> count = new ConcurrentHashMap<String, Integer>();
            count.put("count", 0);
            //开始等待客户端连接
            //实现一个CompletionHandler 的接口,匿名的实现类
            server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
                //缓存区
                final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
                //实现IO操作完成的方法
                @Override
                public void completed(AsynchronousSocketChannel result, Object attachment) {
                    count.put("count", count.get("count") + 1);
                    System.out.println(count.get("count"));
                    try {
                        //清空缓存标记
                        buffer.clear();
                        //读取缓存内容
                        result.read(buffer).get();
                        //写模式转换成读模式
                        buffer.flip();
                        result.write(buffer);
                        buffer.flip();
                    } catch (Exception e) {
                        System.out.println(e.toString());
                    } finally {
                        try {
                            result.close();
                            server.accept(null, this);
                        } catch (Exception e) {
                            System.out.println(e.toString());
                        }
                    }
                }

                //实现IO操作失败的方法
                @Override
                public void failed(Throwable exc, Object attachment) {
                    System.out.println("IO操作是失败: " + exc);
                }
            });
            try {
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException ex) {
                System.out.println(ex);
            }
        } catch (IOException e) {
            System.out.println(e);
        }
    }


}

결과 :

클라이언트

已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
已发送至服务器
获取反馈结果:客户端线程:Thread-22请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-21请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-20请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-19请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-18请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-17请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-16请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-15请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-14请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
获取反馈结果:客户端线程:Thread-13请求服务端                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

서버

服务已启动,监听端口9999
1
2
3
4
5
6
7
8
9
10

개요

달성하기 위해 코드를보고, 나는 종기 불만 너무 여기 싶지 않아, 시청보다 더 편안하게하기 위해 텍스트의 무리가 아닌, 우리 모두가 코드를 보면 그 -
그이 내 요약의 큰 형제를 많이 쓴 볼 정직합니다 일. 드디어, 텍스트 편집자 주를 통해이 일을 이해하기가 어렵 손에서 다음의 동작을 이해하기 위해.

추천

출처www.cnblogs.com/one-reader/p/11469554.html