1:Channel是什么

通道表示与实体的开放连接,例如硬件设备、文件、网络套接字或能够执行一个或多个不同 I/O 操作(例如读取或写入)的程序组件。

1.1:Channel与Stream的对比

1.2:Channel的类型

文件类:

  • FileChannel

可通过 FileInputStream/FileOutputStream 的getChannel方法获取通道。


网络类:

基于socket协议:

  • SocketChannel
  • ServerSocketChannel

可通过 Socket/SocketServer 的getChannel方法获取通道。

基于UDP协议:

  • DatagramChannel

可通过 DatagramSocket 的getChannel方法获取通道。

1.3:操作系统IO演变史

早一代IO操作是由CPU负责IO接口:
Java-NIO之Channel(通道)-LMLPHP

新一代DMA授权处理IO接口:
Java-NIO之Channel(通道)-LMLPHP

通道(Channel)模式:
Java-NIO之Channel(通道)-LMLPHP

通道的产生是由于操作系统的升级而支持的。

2:Channel和操作系统的关系

在操作系统中对IO设备的控制方式一共有四种,按时间线依次是 轮询、中断、DMA、和通道 方式。

  • 轮旋

轮询就是进行IO时操作系统一直问控制器数据准备好了没有。

  • 中断

中断就是异步的方式进行了,CPU向设备控制器发送一条IO指令后接着返回继续做原来的工作,而当设备控制器从设备中取出数据放到控制器的寄存器中后便向CPU发送中断信号,CPU在检查完数据后便向控制器发送取走数据的信号,将数据写入内存,但仍是以字节为单位的。

  • DMA

DMA则是CPU和设备控制器之间的引入的一层加快速度的手段,由DMA代替CPU进行数据传送,CPU将指令发送给DMA,DMA向控制器发送请求,设备控制器将数据从缓冲区将数据直接写入内存。完成后设备控制器发送一个信号给DMA,DMA重复检查数据是否传送完成,确认完成后中断让CPU知道。

DMA比起中断方式已经显著减少了CPU的干预,但是CPU每发出一条IO指令,只能去读写一个连续的数据块,当要读多个数据块并存放到不同的内存区域中去,CPU需要发送多条IO指令及进行多次中断。

  • 通道

IO通道方式是DMA方式的发展,把对一个数据块的干预减少为对一组数据块的干预。


IO通道有三种:

  • 字节多路通道(Byte Multiplexor Channel)
  • 选择通道(Block Selector Channel)
  • 数组多路通道(Block Multiplexor Channel)

至于JAVA的Channel和操作系统的的通道是如何选择通道类型、如何交互的就没法深入了,暂且理解JAVA的Channel是对操作系统的通道的一种抽象实现吧。

3:Channel文件通道

上一篇已经介绍过Channel的文件内存映射(map),就不做介绍了。

所谓的分散读取、聚集写入就是用多个buffer来接收数据、传输数据。
Java-NIO之Channel(通道)-LMLPHP

分散读取、聚集写入代码示例:

    @Test
    public void gatherWrite() {
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            File file = new File("src/test/java/com/loper/mine/SQLParserTest.java");
            inputStream = new FileInputStream(file);
            inChannel = inputStream.getChannel();

            ByteBuffer buffer1 = ByteBuffer.allocate(8);
            ByteBuffer buffer2 = ByteBuffer.allocate(15);
            ByteBuffer[] buffers = new ByteBuffer[]{buffer1, buffer2};

            // 分散读取
            inChannel.read(buffers);
            for (ByteBuffer buffer : buffers) {
                buffer.flip();
                System.out.println(buffer.mark());
            }

            File outFile = new File("src/test/java/com/loper/mine/1.txt");
            outputStream = new FileOutputStream(outFile);
            outChannel = outputStream.getChannel();
            // 聚集写入
            outChannel.write(buffers);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null)
                    inputStream.close();
                if (outputStream != null)
                    outputStream.close();
                if (inChannel != null)
                    inChannel.close();
                if (outChannel != null)
                    outChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

4:Channel网络通道

4.1:socket协议

这部分代码比较复杂,可以翻看我的github代码,这里就不坐介绍了。
地址:https://github.com/zgq7/devloper-mine/tree/master/src/main/java/com/loper/mine/core/socket/nio

4.2:UDP协议

UDP发送数据:

    @Test
    public void send() {
        DatagramChannel channel = null;
        try {
            channel = DatagramChannel.open();
            // 设置为非阻塞
            channel.configureBlocking(false);

            ByteBuffer buffer = ByteBuffer.allocate(1024);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String nextLine = scanner.nextLine();
                buffer.put(nextLine.getBytes());
                buffer.flip();
                channel.send(buffer, new InetSocketAddress("127.0.0.1", 8056));
                buffer.clear();
                if ("over".equals(nextLine))
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (channel != null) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

UDP接收数据:

    @Test
    public void receive() {
        DatagramChannel channel = null;
        try {
            channel = DatagramChannel.open();
            // 设置为非阻塞
            channel.configureBlocking(false);
            channel.bind(new InetSocketAddress(8056));

            Selector selector = Selector.open();
            channel.register(selector, SelectionKey.OP_READ);

            while (true) {
                int select = selector.select();
                boolean exit = false;

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();

                    if (selectionKey.isReadable()) {
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        channel.receive(buffer);
                        buffer.flip();
                        byte[] data = new byte[buffer.limit()];
                        buffer.get(data);
                        String str = new String(data);
                        System.out.println("收到:" + str);
                        if ("over".equals(str))
                            exit = true;
                    }
                    iterator.remove();
                }
                if (exit)
                    break;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (channel != null) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

接收端接收数据并退出:
Java-NIO之Channel(通道)-LMLPHP


以上即为本文理论知识+代码实战全部内容。如有错误欢迎指正。

本文参考文章:

04-11 12:08