美文网首页
JAVA的BIO和NIO

JAVA的BIO和NIO

作者: 东南枝下 | 来源:发表于2022-02-07 15:00 被阅读0次

    回顾telnet命令的使用

    # 连接到主机
    open 127.0.0.1 9000
    

    BIO

    阻塞IO,在建立连接serverSocket.accept()和获取数据clientSocket.getInputStream().read(bytes)处阻塞
    单线程无法处理多个连接,高并发环境需要创建大量线程处理,线程会爆炸式增长

    public class SocketServer {
    
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = new ServerSocket(9000);
            while (true) {
                System.out.println("等待连接...");
                // 没有连接,阻塞在此处
                Socket clientSocket = serverSocket.accept();
                System.out.println("有客户端连接了....");
                handler(clientSocket);
                clientSocket.close();
            }
        }
    
        private static void handler(Socket clientSocket) throws IOException {
            byte[] bytes = new byte[1024];
            System.out.println("准备read...");
            while (true) {
                // 阻塞方法,阻塞在此处
                int read = clientSocket.getInputStream().read(bytes);
                System.out.println("read 完毕...");
    
                if (read != -1) {
                    String inStr = new String(bytes, 0, read);
                    inStr = inStr.trim();
                    System.out.println("客户端接收到到数据  " + inStr);
    
                    if ("exit".equals(inStr)) {
                        break;
                    }
                }
            }
        }
    
    }
    
    

    NIO

    简单NIO

    NIO是通过channel来建立连接,读写数据的
    可以设置serverSocket.configureBlocking(false);使建立连接serverSocket.accept();为非阻塞
    可以设置socketChannel.configureBlocking(false);使读取数据channel.read(byteBuffer);为非阻塞
    单一线程可以处理多个连接
    当连接数巨大时,每次读取需要遍历一个巨大的连接列表

    package nio;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Jenson
     */
    public class NioServer {
    
        /**
         * 保存客户端链接
         */
        static List<SocketChannel> channelList = new ArrayList<>();
    
    
        public static void main(String[] args) throws IOException {
            // 创建NIO
            ServerSocketChannel serverSocket = ServerSocketChannel.open();
            serverSocket.socket().bind(new InetSocketAddress(9000));
            // 设置为非阻塞
            serverSocket.configureBlocking(false);
            System.out.println("服务启动成功");
    
            while (true) {
    
                // 非阻塞在此处不会阻塞
                // NIO非阻塞由操作系统内部实现,底层调用了linux内核的accept函数
                SocketChannel socketChannel = serverSocket.accept();
                if (socketChannel != null) {
                    System.out.println("链接成功");
                    // 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    // 保存客户端链接
                    channelList.add(socketChannel);
                }
    
                // 遍历链接进行数据读取
                for (SocketChannel channel : channelList) {
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    // 非阻塞模式在此处就不会阻塞
                    int len = channel.read(byteBuffer);
    
                    if (len > 0) {
                        System.out.println("接收到到消息为  " + new String(byteBuffer.array()));
                    } else if (len == -1) {
                        // 客户端断开链接
                        channelList.remove(channel);
                    }
    
                }
    
            }
    
        }
    }
    
    

    selector多路复用器

    Channel注册到Selector中,注册时需要指定监听的事件
    Selector相当于一个集合,记录了那些有事件发生的集合
    当有事件发生时selector.select();就会跳出阻塞
    selectionKeySet.iterator()可以获取到发生的事件
    SelectionKey.channel()可以获取到发生事件的Chanel

    package nio;
    
    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.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    
    /**
     * @author Jenson
     */
    public class NioSelectorServer {
    
        public static void main(String[] args) throws IOException {
    
            // 创建NIO
            ServerSocketChannel serverSocket = ServerSocketChannel.open();
            serverSocket.socket().bind(new InetSocketAddress(9000));
            // 设置为非阻塞
            serverSocket.configureBlocking(false);
            // 打开selector处理channel,创建epoll
            Selector selector = Selector.open();
            // 把ServerSocketChannel注册到Selector上
            SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
    
            System.out.println("服务启动成功");
    
    
            while (true) {
                // 等待阻塞需要处理到的事件发生 (SelectionKey.OP_ACCEPT)
                selector.select();
                // 获取selector注册的全部事件的selectionKey实例
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();
                Iterator<SelectionKey> selectionKeyIterator = selectionKeySet.iterator();
                while (selectionKeyIterator.hasNext()) {
                    SelectionKey key = selectionKeyIterator.next();
    
                    if (key.isAcceptable()) {
                        // 如果是OP_ACCEPT,则进行连接获取和事件注册
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = server.accept();
                        socketChannel.configureBlocking(false);
                        // 注册客户端 读事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println("客户端连接成功");
                    } else if (key.isReadable()) {
                        // 如果是OP_READ,则进行读取和打印
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                        int len = socketChannel.read(byteBuffer);
                        if (len > 0) {
                            System.out.println("接收到到消息为  " + new String(byteBuffer.array()));
                        } else if (len == -1) {
                            // 客户端断开链接
                            socketChannel.close();
                        }
                    }
                    selectionKeyIterator.remove();
                }
            }
        }
    }
    
    

    参考学习: https://www.bilibili.com/video/BV1fA41157Ht?share_source=copy_web

    相关文章

      网友评论

          本文标题:JAVA的BIO和NIO

          本文链接:https://www.haomeiwen.com/subject/zkkmkrtx.html