JAVA NIO

简介: 一、基本概念描述什么是NIONIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。

一、基本概念描述

什么是NIO

NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。

流与块的比较

NIO和IO最大的区别是数据打包和传输方式。IO是以的方式处理数据,而NIO是以的方式处理数据。

面向流的IO一次一个字节的处理数据,一个输入流产生一个字节,一个输出流就消费一个字节。为流式数据创建过滤器就变得非常容易,链接几个过滤器,以便对数据进行处理非常方便而简单,但是面向流的IO通常处理的很慢。

面向块的IO系统以块的形式处理数据。每一个操作都在一步中产生或消费一个数据块。按块要比按流快的多,但面向块的IO缺少了面向流IO所具有的有雅兴和简单性

NIO与传统IO的对比

NIO

IO

面向缓冲区Buffer

面向流Stream

双向(基于通道Channel)

单向(分别建立输入流、输出流)

同步非阻塞(non-blocking)

同步阻塞

选择器(Selector,多路复用)

支持字符集编码解码解决方案,支持锁,支持内存映射文件的文件访问接口

NIO基础

缓冲区(Buffer)、通道(Channel)和选择器(Selector)、字符集(Charset)

网络异常,图片无法展示
|

缓冲区Buffer

Buffer是一个对象,它包含一些要写入或读出的数据。在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的

Buffer 读写数据步骤:

  1. 写入数据到 Buffer;
  2. 调用 flip() 方法;
  3. 从 Buffer 中读取数据;
  4. 调用 clear() 方法或者 compact() 方法。

通道Channel

Channel表示到IO设备(如:文件、套接字)的连接,即用于源节点与目标节点的连接,在java NIO中Channel本身不负责存储数据,主要是配合缓冲区,负责数据的传输。

通道的主要实现类

FileChannel类:本地文件IO通道,用于读取、写入、映射和操作文件的通道。 SocketChannel类:网络套接字IO通道,TCP协议,针对面向流的连接套接字的可选择通道(一般用在客户端)。 ServerSocketChannel类:网络通信IO操作,TCP协议,针对面向流的监听套接字的可选择通道(一般用于服务端)。 DatagramChannel类:针对面向数据报套接字的可选择通道,能够发送和接受UDP数据包的Channel。UDP协议,由于UDP是一种无连接的网络协议,只能发送和接受数据包。 以上几个类都实现了java.nio.channels.Channel接口。

选择器Selector

Selector是selectableChannel的多路复用器,用于监控SelectableChannel的IO状况。利用selector可以实现在一个线程中管理多个通道Channel,selector是非阻塞IO的核心。

Selector常用方法

方法

描述

Set< SelectionKey > keys()

所有的SelectionKey集合,代表注册在该Selector上的Channel

selectedKeys()

被选择的SelectionKey集合。返回此Selector的已选择键集

int select()

监控所有注册的Channel,当它们中间有需要处理的IO操作时,该方法返回,并将对应的SelectionKey加入被选择的SelectionKey集合中,该方法返回这些Channel的数量。

int select(long timeout)

可以设置超时时长的select()操作

int selectNow()

执行一个立即返回的select()操作,该方法不会阻塞线程

Selector wakeUp()

使一个还未返回的select()方法立即返回

字符集Charset

CharSet是对java nio编码解码的解决方案,专门负责字符的编码和解码。

编码:字符数组、字符串 ===> 字节数组。 解码:字节数组 ==> 字符数组、字符串

NIO中的读和写示例

public static void copyFileUseNIO(String src,String dst) throws IOException{
            FileInputStream fi=new FileInputStream(new File(src));
            FileOutputStream fo=new FileOutputStream(new File(dst));
            //获得传输通道channel
            FileChannel inChannel=fi.getChannel();
            FileChannel outChannel=fo.getChannel();
            //获得容器buffer
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            while(true){
                //判断是否读完文件
                int eof =inChannel.read(buffer);
                if(eof==-1){
                    break;  
                }
                buffer.flip();
                //开始写
                outChannel.write(buffer);
                //写完要重置buffer
                buffer.clear();
            }
            inChannel.close();
            outChannel.close();
            fi.close();
            fo.close();
}  

NIO中的聊天室示例

服务端

private ServerSocketChannel listenChannel;  //监听通道
    private Selector selector;  //选择器对象
    private static final int PORT = 9999;
    public static void main(String[] args) {
        ServerDemo demo = new ServerDemo();
        demo.listen();
    }
    public ServerDemo() {
        try {
            // 1得到监听通道
            listenChannel = ServerSocketChannel.open();
            // 2得到选择器
            selector = Selector.open();
            // 3绑定端口
            listenChannel.bind(new InetSocketAddress(PORT));
            // 4设置为非阻塞模式
            listenChannel.configureBlocking(false);
            // 5将选择器绑定到监听通道并监听accept事件
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
            printInfo("server ok ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 6干活
    public void listen() {
        try {
            while (true) {
                if (selector.select(3000) == 0) {
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isAcceptable()) {   // 连接请求事件
                        SocketChannel sc = listenChannel.accept();
                        sc.configureBlocking(false);
                        sc.register(selector, SelectionKey.OP_READ);
                        System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了...");
                    }
                    if (key.isReadable()) { // 读取数据事件
                        readMsg(key);
                    }
                    // 把keys删掉,防止重复处理
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 读取到客户端发送过来的消息并广播出去
    public void readMsg(SelectionKey key) throws Exception {
        SocketChannel sc = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        if (sc.read(buffer) > 0) {
            // 打印接收到的消息
            String msg = new String(buffer.array());
            printInfo(msg.trim());
            // 发广播
            broadCast(sc, msg);
        }
    }
    public void broadCast(SocketChannel sc, String msg) throws Exception {
        printInfo("服务器发送了广播...");
        for (SelectionKey key : selector.keys()) {
            SelectableChannel channel;
            if ((channel = key.channel()) instanceof SocketChannel && key.channel() != sc) {
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                ((SocketChannel) channel).write(buffer);
            }
        }
    }
    private void printInfo(String str) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("["+sdf.format(new Date()) + "]->" + str);
    }

客户端

private final String HOST = "127.0.0.1";
    private int PORT = 9999;
    private SocketChannel socketChannel;
    private String userName;
    public ClientDemo() {
        try {
            // 1得到一个网络通道
            socketChannel = SocketChannel.open();
            // 2设置非阻塞方式
            socketChannel.configureBlocking(false);
            // 3服务端IP和端口
            InetSocketAddress inetSocketAddress = new InetSocketAddress(HOST, PORT);
            // 4连接服务器
            if (!socketChannel.connect(inetSocketAddress)) {
                while (!socketChannel.finishConnect()) {
                    System.out.println("客户端一直在连接.....");
                }
            }
            // 5得到客户端IP和端口,作为聊天用户名
            userName = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println("-------------client: " + userName + " is ready-------------");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 向服务器发送数据
    public void sendMsg(String msg) throws Exception {
        if (msg.equalsIgnoreCase("bye")) {
            socketChannel.close();
            return;
        }
        msg = userName + "say: " + msg;
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer);
    }
    // 从服务端接收数据
    public void receiveMsg() throws Exception {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        if (socketChannel.read(buffer) > 0) {
            String msg = new String(buffer.array());
            System.out.println(msg.trim());
        }
    }
    public static void main(String[] args) throws Exception {
        ClientDemo chatClient = new ClientDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        chatClient.receiveMsg();
                        Thread.sleep(3000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNextLine()) {
            chatClient.sendMsg(scanner.nextLine());
        }
    }



相关文章
|
4月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
11天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
1月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
28 2
|
2月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
3月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
81 2
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
113 0
|
4月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
165 1
|
3月前
|
存储 网络协议 Java
【Netty 神奇之旅】Java NIO 基础全解析:从零开始玩转高效网络编程!
【8月更文挑战第24天】本文介绍了Java NIO,一种非阻塞I/O模型,极大提升了Java应用程序在网络通信中的性能。核心组件包括Buffer、Channel、Selector和SocketChannel。通过示例代码展示了如何使用Java NIO进行服务器与客户端通信。此外,还介绍了基于Java NIO的高性能网络框架Netty,以及如何用Netty构建TCP服务器和客户端。熟悉这些技术和概念对于开发高并发网络应用至关重要。
70 0
|
4月前
|
安全 Java
【Java】已解决java.nio.channels.OverlappingFileLockException异常
【Java】已解决java.nio.channels.OverlappingFileLockException异常
120 1
|
4月前
|
Java
【Java】已解决java.nio.channels.ClosedChannelException异常
【Java】已解决java.nio.channels.ClosedChannelException异常
385 1