你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

1.彻底明白java nio和bio

2021/12/26 1:10:25

1. Java NIO是非阻塞IO,有些人可能会把NIO理解成 new io,其实不是的,nio和阻塞IO本质上操作的socket文件描述符是相同的,SocketChannel和socket本质也是一样的,都是要读取socket数据。NIO和阻塞IO有什么区别呢。阻塞IO的特点是当socket没有数据的时候,read会线程阻塞,直到有可读的数据,才获取线程的执行权,没可读的数据线程就一直卡在read的方法上。NIO是我不调read方法,直到操作系统告诉我那个socket是有数据的可读的,我再去read,这样我是不是就不会卡死在read上。

NIO和阻塞IO的区别:

阻塞IO:当读取一个socket的时候,没有数据可读,线程卡死,直到有数据线程才恢复运行。
当server要监听多个socket描述符的时候server就废了,一个卡住就相当于线程死了,
除非用上多线程,不过线程终究是有限的。

NIO:我不会先read,而是我先获取哪些socket是可以读的,一个都没有我就等待,
直到有可读的socket,我遍历可读的socket集合,挨个处理。我不会卡在任何一个socket read上面
linux上面实现nio使用的,epoll的三个函数实现的。关于Linux的C实现可以看我上一篇的文章。

2. java nio server

流程和我上一篇的 epoll http server几乎是一样的,懂得底层原理,再看这些和玩一样。

@Slf4j
public class NioServer {
    public static void main(String[] args) throws Exception{
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        log.info("http://localhost:8080");
        while (true){
            // select 就会阻塞,直到有新的连接或者已连接的文件描述符触发事件,返回的set集合是这个socket有注册的事件发生
            int select = selector.select();
            log.info("socket size: "+select);
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                try{
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()){
                        ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel channel = serverSocketChannel1.accept();
                        log.info("new socket: "+channel.getRemoteAddress().toString());
                        channel.configureBlocking(false);
                        channel.register(selector, SelectionKey.OP_READ);
                    }
                    if (selectionKey.isReadable()){
                        SocketChannel channel = null;
                        try{
                            channel = (SocketChannel) selectionKey.channel();
                            ByteBuffer byteBuffer = ByteBuffer.allocate(8192);
                            channel.read(byteBuffer);
                            byteBuffer.flip();
                            byte[] array = new byte[byteBuffer.limit()];
                            byteBuffer.get(array);
                            log.info("data:" + new String(array, StandardCharsets.UTF_8));
                            byteBuffer.clear();
                            //写数据
                            byteBuffer.put("hello".getBytes(StandardCharsets.UTF_8));
                            byteBuffer.flip();
                            channel.write(byteBuffer);
                            byteBuffer.clear();
                        }catch (Exception e){
                            // 可能会抛出IOException,这个时候关闭这个socket
                            log.error(e.getMessage(), e);
                            assert channel != null;
                            channel.close();
                        }
                    }
                }catch (Exception e){
                    log.error(e.getMessage(), e);
                    System.exit(-1);
                }finally {
                    iterator.remove();
                }
            }
        }
    }
}

3. socket client

public class NioClient {
    public static void main(String[] args) throws Exception{
        SocketChannel socket = SocketChannel.open(new InetSocketAddress("localhost", 8080));
        DefaultEventLoop eventExecutors = new DefaultEventLoop();
        eventExecutors.submit(() -> {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true){
                try {
                    socket.read(buffer);
                    buffer.flip();
                    byte[] bytes = new byte[buffer.limit()];
                    buffer.get(bytes);
                    System.out.println(new String(bytes, StandardCharsets.UTF_8));
                } catch (IOException e) {
                    e.printStackTrace();
                    socket.close();
                    System.exit(-1);
                }finally {
                    buffer.clear();
                }
            }
        });
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (true){
            Scanner scanner = new Scanner(System.in);
            String s = scanner.nextLine();
            buffer.put(s.getBytes(StandardCharsets.UTF_8));
            buffer.flip();
            socket.write(buffer);
            buffer.clear();
        }
    }
}

4. 测试