NIO与BIO的区别

在以前搭建网络连接中,使用的BIO,在用户连接后专门创建一个线程和用户对接当用户没有使用时就会阻塞线程这也就是BIO为什么叫阻塞式通讯,而在今天高并发的情况下BIO会占用大量资源,阻塞线程会消耗系统资源形成一种浪费。所以NIO就是为了解决这个问题。

在传统的BIO模式中会使用线程来连接客户端,在一定程度上避免线程频繁的创建与销毁减少系统资源的使用,但是这仍然存在很大的问题。当用户离线时,线程不会直接注销,而是会阻塞或者空转,一直占用着线程资源,随着用户连接数越来越多所需要的线程资源就会越来越多降低整体新能,当有上万用户时所需要的资源就会十分夸张。但是NIO很好的避免了这个问题,下面来介绍NIO的组件。

NIO在Java中由三个组件构成分别为:缓存(ByteBuffer),管道(Channel)和选择器(Selector)。最为关键的选择器,选择器通过注册客户端发送过来的管道,之后直接进入休眠,等到有请求发送在开始读取连接管道中存在的数据处理好后再用管道原路返回。

接着是管道和缓存的使用,下面有一个服务端利用管道和缓存来实现NIO连接的例子:

复制代码
        int port=8080;
        ServerSocketChannel server=ServerSocketChannel.open();
        server.configureBlocking(false);
        server.bind(new InetSocketAddress(port));
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        while (true) {
            Thread.sleep(1000);
            SocketChannel socketChannel = server.accept();
            if (socketChannel != null) {
                System.out.println("连接成功。。。");
                Thread.sleep(1000);
                int num=socketChannel.read(buffer);
                if(num>0) {
                    buffer.flip();
                    byte[] bytes=new byte[buffer.remaining()];
                    buffer.get(bytes);
                    System.out.println("client:"+new String(bytes));
                }
                buffer.clear();
                String msg = "连接成功。。。";
                buffer.put(msg.getBytes());
                buffer.flip();
                socketChannel.write(buffer);
                buffer.clear();
            }
        }

这里先创建一个端口用于接收在这个端口下创建的客户端,接着使用一个缓存空间接收管道中的数据,这里没有使用选择器而是采用轮询的方式效率会不如选择器。

客户端则需要创建管道和缓存空间来将需要的数据传给服务器或从服务器那里读取数据,实现如下:

复制代码
        String ip="127.0.0.1";
        int port=8080;
        SocketChannel channel=SocketChannel.open();
        channel.configureBlocking(false);//禁止阻塞
        channel.connect(new InetSocketAddress(ip,port));
        ByteBuffer buffer=ByteBuffer.allocate(1024);

        while (!channel.finishConnect()){
            System.out.println("客户端正在连接服务器。。。");
        }
        Scanner scanner=new Scanner(System.in);
        String message=scanner.nextLine();
        buffer.put(message.getBytes());
        buffer.flip();//切换为读模式
        Thread.sleep(100);
        channel.write(buffer);
        buffer.clear();

        int readNum=channel.read(buffer);
        if(readNum>0){//只有大于0才读到数据,-1时断开连接
            buffer.flip();
            byte[] bytes=new byte[buffer.remaining()];
            buffer.get(bytes);//将读到的数据存入自定义数组中
            String msg=new String(bytes);
            System.out.println("client:"+msg);
        }

这里需要用while循环来连接至客户端防止一次没连接上,利用缓存将字符串转为字节流,再将字节流输入管道中,服务器通过轮询来获取到数据并利用管道返回结果。

相关推荐
0x537 分钟前
Java网络编程(四)
java·网络
ly768918 分钟前
Java 设计模式详解:从原则到 23 种经典模式
java·开发语言·设计模式
highreport24 分钟前
HighReport报表工具定时调度和邮件定时发送
运维·服务器·数据库
萧瑟余晖35 分钟前
Java深入解析篇三十之Java日志框架
java·开发语言
TonyLee01740 分钟前
Claude Code + DeepSeek:服务器安装与使用笔记
linux·服务器·deepseek·claude code
St_rive41 分钟前
Pytest skip&&skipif跳过⽤例
运维·服务器·pytest
buhuizhiyuci1 小时前
【Linux 篇】数字世界的水流蓄水池 —— IO 缓冲区机制深度实战解析
linux·运维·服务器·c++
骇客野人1 小时前
Linux 服务器 Python 版 MCP 服务部署整体方案(适配 Claude/Cursor/自研Agent)
linux·服务器·python
Suxing91 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法
减瓦1 小时前
别让 Java 版本选择变成生产事故
java