【JAVA】BIO、NIO、AIO(Socket-IO模型)

同步与异步、阻塞与非阻塞

同步操作:一个任务完成之前,必须等待,不能做其他操作;
异步操作:一个任务完成之前,可以先做其他操作;
阻塞:对于CPU执行,挂起当前线程进行等待,不能做其他操作
非阻塞:对于CPU执行,不需要挂起当前线程,可以先执行其他操作;

BIO(Blocking I/O)

  • 同步、阻塞的IO模型,客户端与服务器一个连接对应服务器的一个线程。每当客户端有连接请求到服务器时,服务器需要启动一个线程进行处理,没有处理完成时,线程不能做其他操作。
  • 适用范围:JDK1.4之前的唯一选择,只适用于连接数比较小且固定的架构,对于服务器资源要求较高;
    在这里插入图片描述
//服务端代码,传统IO
public class BioSocketServerDemo {
    public static void main(String[] args) {
        int port = 9696;
        Thread serverThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    ServerSocket serverSocket = new ServerSocket(port);
                    while(true){
                        Socket socket = serverSocket.accept();
                        Thread serverHandler = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    System.out.println("服务端收到客户端请求!!");
                                    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                                    br.lines().forEach(s-> System.out.println("收到客户端消息:" + s));
                                    socket.shutdownInput();
                                    try {
                                        Thread.sleep(100);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                                    bw.write("服务端正在运行!");
                                    bw.write("Server Time = "+System.currentTimeMillis());
                                    bw.flush();
                                    socket.shutdownOutput();
                                    socket.close();
                                    System.out.println("服务端发送消息回应客户端!!");
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                        serverHandler.start();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        serverThread.start();
        System.out.println("服务端已启动!!");
    }
}

//客户端代码
public class BioSocketClientDemo {
    public static void main(String[] args){
        try {
            Socket socket = new Socket(InetAddress.getLocalHost(),9696);
            BufferedWriter cbw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            cbw.write("客户端请求服务器端!!");
            cbw.write("client time: "+System.currentTimeMillis());
            cbw.flush();
            socket.shutdownOutput();
            System.out.println("客户端发送消息成功!!");
            BufferedReader cbr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            cbr.lines().forEach(s-> System.out.println("客户端收到消息:" + s));
            socket.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

NIO(Nio-blocking I/O)

JDK1.4版本,java.nio.* 包中被引入的JAVA I/O类库,目的在于提高I/O的速度,包括文件I/O和网络I/O,部分旧的I/O包也使用nio重新实现。

旧的I/O类FileInputStream、FileOutputStream、RandomAccessFile这三个字节操纵流,可以产生FileChannel;与底层的noi性质一致,Reader以及Writer这种字符流不能用于产生FileChannel,但是java.nio.channels.Channels类中提供了方法可以产生Reader和Writer;

  • 同步、非阻塞的IO模型,服务器启动一个线程,将所有客户端的连接请求注册到多路复用器(Selector)上,多路复用器轮询到有IO请求时才会启动一个线程处理;
  • 适用范围:适用于连接数据多且连接比较短的架构。

NIO的基石

通道(Channel)

  • 实现Channel接口的子类,可以通过它读取和写入数据,NIO中均是将数据先写入包含一个或多个字节的缓冲区,然后将缓冲区的数据写入通道中,或是将通道中的数据获取到缓冲区中,在从缓冲区中获取数据;
  • 与流(Stream)的区别:①流的读写是单向的(一个流只能或读或写),通道的读写是双向的(一个通道可读可写);②流读写是阻塞的,通道可以异步读写;

选择器(Selector)

Selector是通道的集合,每次服务端接收Socket请求或者客户端进行Socket请求产生的通道,我们均会将它注册到Selector中并设置状态,并在死循环中,根据Selector中注册的Channel的状态对于Channel进行相应的操作,操作完成后,重新调整通道的状态注册到Selector中或者关闭通道或者退出死循环;

缓冲区(ByteBuffer)

ByteBuffer,唯一能直接与通道交互的缓冲器,内部用字节数组存储字节,包含从通道获取或者要写入通道的数据;

详细了解ByteBuffer,参考 https://www.jianshu.com/p/ebc52832dca0

NIO的Socket模型:

在这里插入图片描述

//客户端代码
public class NioSocketClientDemo {
    public static void main(String[] args) {
        try {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 9797));
            Selector selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (true) {
                selector.select();
                Set<SelectionKey> keySet = selector.selectedKeys();
                Iterator<SelectionKey> iterator = keySet.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isConnectable()) {
                        SocketChannel connectChannel = (SocketChannel) key.channel();
                        connectChannel.finishConnect();
                        System.out.println("连接服务端成功!!");
                        connectChannel.register(selector, SelectionKey.OP_WRITE);
                        continue;
                    }
                    if (key.isWritable()) {
                        SocketChannel writeChannel = (SocketChannel) key.channel();
                        String message = new Scanner(System.in).nextLine();
                        ByteBuffer write = ByteBuffer.allocate(1024);
                        String sendMessage = message + System.currentTimeMillis();
                        write.put(sendMessage.getBytes());
                        write.flip();
                        writeChannel.write(write);
                        System.out.println("客户端发送消息成功!!");
                        writeChannel.register(selector, SelectionKey.OP_READ);
                        continue;
                    }
                    if (key.isReadable()) {
                        SocketChannel readChannel = (SocketChannel) key.channel();
                        ByteBuffer read = ByteBuffer.allocate(1024);
                        int num = readChannel.read(read);
                        System.out.println("收到服务端返回消息");
                        System.out.println("服务端返回消息:" + new String(read.array(), 0, num));
                        readChannel.register(selector,SelectionKey.OP_WRITE);
                    }
                }
            }
        } catch (IOException e) {
           e.printStackTrace();
        }
    }
}

//服务端代码
public class NioSocketServerDemo {

    public static void main(String[] args) {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置非阻塞
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress("localhost", 9797));
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端启动成功!!");
            while (true) {
                selector.select();
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()) {
                        ServerSocketChannel socketChannel = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel socket = socketChannel.accept();
                        socket.configureBlocking(false);
                        socket.register(selector, SelectionKey.OP_READ);
                        System.out.println("客户端请求服务端连接成功,服务端时间:" + System.currentTimeMillis());
                    }
                    if (selectionKey.isReadable()) {
                        SocketChannel readChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        int num = readChannel.read(byteBuffer);
                        String message = new String(byteBuffer.array(), 0, num);
                        System.out.println("服务端收到客户端消息:" + message);
                        readChannel.register(selector, SelectionKey.OP_WRITE);
                    }
                    if (selectionKey.isWritable()) {
                        SocketChannel writeChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer writer = ByteBuffer.allocate(1024);
                        String sendMessage = "服务端返回消息,服务端时间" + System.currentTimeMillis();
                        writer.put(sendMessage.getBytes());
                        writer.flip();
                        writeChannel.write(writer);
                        System.out.println("服务端发送消息成功!!");
                        writeChannel.register(selector,SelectionKey.OP_READ);
                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            System.out.println("e.getStackTrace() = " + e);
        }
    }
}

AIO(asynchronous I/O)

  • 异步、非阻塞的IO模型,服务器对于一个有效请求启动一个线程,客户端的I/O请求都是由操作系统先完成I/O之后再回调通知服务器启动线程继续处理的;
  • 适用范围:JDK1.7版本之后JAVA才开始支持,作为NIO的附属品,解决IO不能异步的实现,适用于服务器连接数目多且连接长的架构;
//AIO服务端代码
public class AioSocketServerDemo {

    public static void main(String[] args) throws InterruptedException {
        try {
            AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(9494));
            serverSocketChannel.accept(serverSocketChannel, new CompletionHandler<AsynchronousSocketChannel, Object>() {
                @Override
                public void completed(AsynchronousSocketChannel clientChannel, Object serverChannel) {
                    try {
                        //没有阻塞,继续监听端口接受其他socket连接请求
                        serverSocketChannel.accept(serverChannel, this);
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        clientChannel.read(buffer, null, new CompletionHandler<Integer, Object>() {
                            @Override
                            public void completed(Integer result1, Object object) {
                                buffer.flip();
                                System.out.println("客户端:" + new String(buffer.array(), 0, buffer.limit()));
                                String message = "你好,客户端,服务端已收到请求!接收时间为:" + System.currentTimeMillis();
                                ByteBuffer write = ByteBuffer.allocate(message.getBytes().length);
                                write.put(message.getBytes());
                                write.flip();
                                clientChannel.write(write, null, new CompletionHandler<Integer, Object>() {
                                    @Override
                                    public void completed(Integer result1, Object attachment) {
                                        if (write.hasRemaining()) {
                                            clientChannel.write(write, null, this);
                                        }
                                    }

                                    @Override
                                    public void failed(Throwable exc, Object attachment) {
                                        System.out.println(" 服务端回应消息失败:" + exc.getMessage());
                                    }
                                });
                            }

                            @Override
                            public void failed(Throwable exc, Object attachment) {
                                System.out.println(" 服务端接收消息失败:" + exc.getMessage());
                            }
                        });
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

                @Override
                public void failed(Throwable exc, Object attachment) {
                    serverSocketChannel.accept(null, this);
                    System.out.println("服务端错误:" + exc.getMessage());
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("服务已启动");
        Thread.sleep(Integer.MAX_VALUE);
    }
}

//AIO客户端代码    
public class AioSocketClientDemo {
    public static void main(String[] args) throws InterruptedException {
        try {
            AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
            client.connect(new InetSocketAddress("localhost", 9494), null, new CompletionHandler<Void, Object>() {
                @Override
                       String message = "Hi,服务端,客户端向你发送请求,客户端时间:" + System.currentTimeMillis();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                    byteBuffer.put(message.getBytes());
                    byteBuffer.flip();
                    client.write(byteBuffer, byteBuffer, new CompletionHandler<Integer, Object>() {
                        @Override
                        public void completed(Integer result, Object attachment) {
                            if (byteBuffer.hasRemaining()) {
                                client.write(byteBuffer, null, this);
                            } else {
                                ByteBuffer read = ByteBuffer.allocate(1024);
                                client.read(read, null, new CompletionHandler<Integer, Object>() {
                                    @Override
                                    public void completed(Integer result, Object attachment) {
                                        read.flip();
                                        String message = new String(read.array());
                                        System.out.println("服务端:" + message);
                                    }

                                    @Override
                                    public void failed(Throwable exc, Object attachment) {
                                        System.out.println("客户端从接收信息异常 = " + exc.getMessage());
                                    }
                                });
                            }
                        }

                        @Override
                        public void failed(Throwable exc, Object attachment) {
                            System.out.println("客户端发送请求异常:" + exc.getMessage());
                        }
                    });
                }

                @Override
                public void failed(Throwable exc, Object attachment) {
                    System.out.println("exc.getMessage() = " + exc.getMessage());
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        //让当前主进程无限休眠,主线程执行完,程序退出
        Thread.sleep(Integer.MAX_VALUE);
    }
}
  • AsynchronousSocketChannel的的用法与Socket类似,主要有三个方法connect()、read()、write()但是不同的是每个方法又分为Future版本与CompletionHandler版本;
  • 使用AsynchronousSocketChannelChannel时,accept()、connect()、read()、write()等方法都不会阻塞,但是当使用返回Future类型的方法,并不是方法执行完的时候就成功IO,必须要使用Future.get方法,等get方法的阻塞结束后才能确保IO完成,继续执行下面的操作;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值