关于在网络中使用BIO、NIO、AIO的示例

关于在网络中使用BIO、NIO、AIO的示例

BIO 概述与示例

  • 传统的单通道直连I/O,简单,但是阻塞成本高,主要阻塞点在:
    • 建立连接:new Socket()、server.accept()
    • 读:in.read()
    • 写:out.write()
  • 特征:不管Thread是否有空,都需要为每个Socket的连接、读、写操作进行等待
  • BIO 服务端示例 (ServerDemo.java)
    import com.skey.demo.util.IOUtils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * Description: BIO - 服务端示例
     * <br/>
     * Date: 2019/12/26 11:45
     *
     * @author ALion
     */
    public class ServerDemo {
    
        public static void main(String[] args) {
            ServerSocket server = null;
            try {
                server = new ServerSocket();
                server.bind(new InetSocketAddress(23333));
    
                // BIO 每一个socket连接都会开启一个Thread
                while (true) {
                    // 阻塞
                    Socket socket = server.accept();
    
                    // 启动Thread处理数据
                    new Thread(() -> handle(socket)).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(server);
            }
        }
    
        private static void handle(Socket socket) {
            // 单通道: 此处先读,再写
            InputStream in = null;
            OutputStream out = null;
            try {
                // 读取数据
                in = socket.getInputStream();
                byte[] buffer = new byte[1024];
                // read -> 阻塞式读取(如果Client没写完,会阻塞)
                while (in.read(buffer) != -1) {
                    System.out.println(new String(buffer));
                }
    
                // 写出数据
                out = socket.getOutputStream();
                // write -> 阻塞式写出(如果Client没读完,会阻塞)
                out.write("World".getBytes());
                out.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(out, in);
            }
        }
    
    }
    
  • BIO 客户端示例 (ClientDemo.java)
    import com.skey.demo.util.IOUtils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    /**
     * Description: BIO - 客户端示例
     * <br/>
     * Date: 2019/12/26 12:06
     *
     * @author ALion
     */
    public class ClientDemo {
    
        public static void main(String[] args) {
            Socket socket = null;
            OutputStream out = null;
            InputStream in = null;
            try {
                // 与Server建立连接
                socket = new Socket("127.0.0.1", 23333);
    
                // 发送数据
                out = socket.getOutputStream();
                // write -> 阻塞式写出(如果Server没读完,会阻塞)
                out.write("Hello".getBytes());
                out.flush();
                // 表明已发送完数据,否则Server的InputStream调用read会一直阻塞
                socket.shutdownOutput();
    
                // 接收响应
                in = socket.getInputStream();
                byte[] buffer = new byte[1024];
                // read -> 阻塞式读取(如果Server没写完,会阻塞)
                while (in.read(buffer) != -1) {
                    System.out.println(new String(buffer));
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(in, out, socket);
            }
        }
    
    }
    

NIO 概述与示例

  • NIO即non-blocking IO 或者 new IO,是对于传统BIO几个阻塞点的优化,主要逻辑是:
    • 通过Selector轮询管理channel的连接、读、写状态
    • 再由其他线程根据channel的状态进行不同的处理
  • 特征:单独的Thread管理Socket的连接、读、写状态,其他Thread根据状态进行处理
  • NIO 服务端示例 (ServerDemo.java)
    import com.skey.demo.util.IOUtils;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    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;
    
    /**
     * Description: NIO - 服务端示例
     * <br/>
     * Date: 2019/12/26 12:48
     *
     * @author ALion
     */
    public class ServerDemo {
    
        public static void main(String[] args) {
            ServerSocketChannel ssc = null;
            try {
                // 构建 ServerSocketChannel
                ssc = ServerSocketChannel.open();
                ssc.socket().bind(new InetSocketAddress(23333));
                ssc.configureBlocking(false); // 设置非阻塞
    
                // 创建用于接收连接的Selector
                Selector selector = Selector.open();
                ssc.register(selector, SelectionKey.OP_ACCEPT);
    
                // 轮询select,看是否有数据
                while (selector.select() > 0) {
                    // 遍历 keys
                    Set<SelectionKey> keySet = selector.selectedKeys();
                    Iterator<SelectionKey> iter = keySet.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
    
                        // 处理 key
                        handle(key);
    
                        // 移除key,防止后续重复处理
                        iter.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(ssc);
            }
        }
    
        /**
         * 根据key的状态进行处理
         */
        private static void handle(SelectionKey key) {
            if (key.isAcceptable()) { // 如果是可接收的
                System.out.println("handle: key.isAcceptable()");
    
                // 获取Channel
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                try {
                    SocketChannel channel = ssc.accept();
                    channel.configureBlocking(false); // 设置为非阻塞
    
                    // 标识channel为可读状态
                    channel.register(key.selector(), SelectionKey.OP_READ);
                    System.out.println("channel -> OP_READ");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (key.isReadable()) { // 如果是可读的
                System.out.println("handle: key.isReadable()");
    
                // 取消READ事件
                key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
    
                // 交给线程池处理
                PoolEngine pool = PoolEngine.getInstance();
                pool.execute(new ServerHandler(key));
            }
        }
    
    }
    
  • NIO 服务端数据处理类 (ServerHandler.java)
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.SocketChannel;
    
    /**
     * Description: Server 数据处理
     * <br/>
     * Date: 2019/12/26 16:56
     *
     * @author ALion
     */
    public class ServerHandler implements Runnable {
    
        private SelectionKey key;
    
        public ServerHandler(SelectionKey key) {
            this.key = key;
        }
    
        @Override
        public void run() {
            try {
                // 获取Channel
                SocketChannel channel = (SocketChannel) key.channel();
    
                // 读取数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int len = 0;
                while ((len = channel.read(buffer)) > 0) {
                    buffer.flip();
                    System.out.println("msg = " + new String(buffer.array(), 0, len));
                    buffer.clear();
                }
    
                if (len == -1) {
                    channel.close();
                    System.out.println("channel -> close");
                } else {
                    // 添加READ事件
                    key.interestOps(key.interestOps() | SelectionKey.OP_READ);
    //                        key.selector().wakeup();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    
  • NIO 客户端示例 (ClientDemo.java)
    import com.skey.demo.util.IOUtils;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    
    /**
     * Description: NIO - 客户端示例
     * <br/>
     * Date: 2019/12/26 14:07
     *
     * @author ALion
     */
    public class ClientDemo {
    
        public static void main(String[] args) {
            InetSocketAddress address = new InetSocketAddress("127.0.0.1", 23333);
            SocketChannel channel = null;
            try {
                channel = SocketChannel.open(address);
                channel.configureBlocking(false); // 设置为非阻塞
    
                // 发送消息
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                buffer.put("hello world".getBytes());
                buffer.flip();
                channel.write(buffer);
                buffer.clear();
    
                channel.shutdownOutput();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(channel);
            }
    
        }
    
    }
    

AIO 概述与示例

  • AIO即异步IO,或叫做NIO2,通过系统底层进行优化,相对于NIO其关键点在于:
    • 通过监听系统底层IO连接触发后续处理,不再使用Selector轮询
    • Linux底层因为是epoll,操作和Selector相似,所以AIO在Linux系统上性能相差不大
  • 特征:监控系统底层IO连接,再由Thread进行处理
  • AIO 服务端示例 (ServerDemo.java)
    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.concurrent.CountDownLatch;
    import java.util.concurrent.Executors;
    
    /**
     * Description: AIO - 服务端示例
     * <br/>
     * Date: 2019/12/26 21:27
     *
     * @author ALion
     */
    public class ServerDemo {
    
        public static void main(String[] args) {
            try {
                AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withCachedThreadPool(
                        Executors.newCachedThreadPool(), 4
                );
                AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(channelGroup);
                server.bind(new InetSocketAddress(23333));
    
                CountDownLatch latch = new CountDownLatch(100);
    
                // 操作系统会回调此处
                server.accept(
                        null,
                        new CompletionHandler<AsynchronousSocketChannel, Object>() {
                            @Override
                            public void completed(AsynchronousSocketChannel channel, Object attachment) {
                                //
                                server.accept(null, this);
    
                                handle(channel);
    
                                // 每成功处理一次,锁减1
                                latch.countDown();
                            }
    
                            @Override
                            public void failed(Throwable exc, Object attachment) {
                                exc.printStackTrace();
                            }
                        });
    
                try {
                    // 防止主线程直接结束
                    // 阻塞,直到完成指定次数的server.accept回调
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private static void handle(AsynchronousSocketChannel channel) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            channel.read(
                    buffer,
                    buffer,
                    new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer attachment) {
                            attachment.flip();
                            System.out.println(new String(attachment.array(), 0, result));
    
                            // 向客户端发送消息
                            channel.write(ByteBuffer.wrap("Server: world".getBytes()));
                        }
    
                        @Override
                        public void failed(Throwable exc, ByteBuffer attachment) {
    
                        }
                    }
            );
        }
    
    }
    
  • AIO 客户示例 (ClientDemo.java)
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.AsynchronousSocketChannel;
    import java.nio.channels.CompletionHandler;
    import java.util.concurrent.ExecutionException;
    
    /**
     * Description: AIO - 客户端示例
     * <br/>
     * Date: 2019/12/26 23:06
     *
     * @author ALion
     */
    public class ClientDemo {
    
        public static void main(String[] args) {
            AsynchronousSocketChannel channel;
            try {
                channel = AsynchronousSocketChannel.open();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                channel.connect(
                        new InetSocketAddress("127.0.0.1", 23333),
                        buffer,
                        // 当连接创建成功或失败时会调用
                        new CompletionHandler<Void, ByteBuffer>() {
                            @Override
                            public void completed(Void result, ByteBuffer attachment) {
                                System.out.println("----connection is successfully----");
                                try {
                                    // 向Server发送消息
                                    channel.write(ByteBuffer.wrap("Client: hello".getBytes())).get();
    
                                    // 读取Server传回来的消息
                                    channel.read(attachment).get();
                                    attachment.flip();
                                    System.out.println(new String(attachment.array()));
                                } catch (InterruptedException | ExecutionException e) {
                                    e.printStackTrace();
                                }
                            }
    
                            @Override
                            public void failed(Throwable exc, ByteBuffer attachment) {
    
                            }
                        }
                );
    
                // 防止主线程直接结束
                Thread.sleep(10000);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    

附:代码相关工具类

  • IOUtils.java
    public class IOUtils {
    
        public static void close(AutoCloseable... closeables) {
            for (AutoCloseable closeable : closeables) {
                if (closeable != null) {
                    try {
                        closeable.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
  • PoolEngine.java
    import java.util.concurrent.*;
    
    public class PoolEngine {
    
        private PoolEngine() {
        }
    
        public static PoolEngine getInstance() {
            return Inner.instance;
        }
    
        private static class Inner {
            private static final PoolEngine instance = new PoolEngine();
        }
    
        private static int corePoolSize = 4;
    
        private static int maximumPoolSize = 8;
    
        private static long keepAliveTime = 100;
    
        private static int queueCapacity = 1024;
    
        private ExecutorService pool = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(queueCapacity),
                new ThreadPoolExecutor.AbortPolicy());
    
        public void execute(Runnable runnable) {
            if (pool != null) {
                pool.execute(runnable);
            }
        }
    
        public <T> Future<T> submit(Callable<T> callable) {
            if (pool != null) {
                return pool.submit(callable);
            } else {
                return null;
            }
        }
    
        public void shutdown() {
            if (pool != null) {
                pool.shutdown();
                pool = null;
            }
        }
    
    }
    
发布了128 篇原创文章 · 获赞 45 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/alionsss/article/details/103724561