Java NIO 总结: Channel 通道

2024-07-21 1357阅读
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」

☠博主专栏 :

♝博主的话 : 搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基


这里写目录标题

      • 前言
      • 一、Channel的基本概念和作用
      • 二、Channel的工作原理
      • 三、Channel的基本使用
      • 四、Channel的四种类型
        • 1. FileChannel
        • 2. SocketChannel
        • 3. ServerSocketChannel
        • 4. DatagramChannel
        • 五、通道与通道之间发送数据
        • 总结

          前言

          在Java NIO中,Channel是一个核心概念,它表示一个打开的连接,可以连接到I/O设备(如磁盘文件、Socket)或者一个支持I/O访问的应用程序。与传统的IO操作相比,NIO通过Channel和Buffer相结合,提高了IO性能和数据传输效率。

          一、Channel的基本概念和作用

          Channel是Java NIO中的一个接口,它定义了与I/O设备交互的基本方法,如读取、写入和关闭等。在Java NIO中,Channel主要有四种类型:FileChannel、SocketChannel、ServerSocketChannel和DatagramChannel,它们分别对应着文件I/O和网络通信的不同场景。

          Channel在 NIO中起着至关重要的作用,它提供了一种高效、非阻塞的IO操作方式。通过Channel和Buffer的结合使用,减少了数据在内存和磁盘之间的复制次数,提高了数据传输效率。这使得Java NIO能够支持更大的数据量和更高的并发性能,为大规模并发网络编程提供了有效的解决方案。

          二、Channel的工作原理

          在Java NIO中,所有的IO操作都是从Channel开始的。读取操作即从Channel读到Buffer,写操作即从Buffer写入Channel。这种数据传输方式减少了数据在内存和磁盘之间的复制次数,提高了数据传输效率。

          三、Channel的基本使用

          使用FileChannel读取文件的代码:

          try (FileChannel fileChannel = new RandomAccessFile("example.txt", "r").getChannel()) {
              ByteBuffer buffer = ByteBuffer.allocate(1024);
              int bytesRead = 0;
              while ((bytesRead = fileChannel.read(buffer)) != -1) {
                  buffer.flip();
                  while (buffer.hasRemaining()) {
                      System.out.print((char) buffer.get());
                  }
                  buffer.clear();
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          

          使用FileChannel来读取一个文件,并通过ByteBuffer来暂存读取到的数据。

          四、Channel的四种类型

          [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LhNtfNMx-1721094073105)(https://i-blog.csdnimg.cn/direct/8522433114e046c18751bbc90f4f1922.jpeg#pic_center)]

          1. FileChannel

          作用:FileChannel是用于文件的数据读写的通道。它允许应用程序从文件中读取数据,也可以将数据写入文件中。

          使用:

          • 获取FileChannel:通过RandomAccessFile的getChannel()方法获取。
          • 读取数据:使用read()方法从文件中读取数据到ByteBuffer缓冲区。
          • 写入数据:使用write()方法将数据写入文件。
          • 关闭通道:使用close()方法关闭通道。

            代码:

            try (FileChannel fileChannel = new RandomAccessFile("example.txt", "r").getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int bytesRead = 0;
                while ((bytesRead = fileChannel.read(buffer)) != -1) {
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        System.out.print((char) buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            2. SocketChannel

            作用:SocketChannel是用于TCP套接字TCP连接的数据读写的通道。

            使用:

            • 获取SocketChannel:使用SocketChannel.open()方法创建套接字传输通道。
            • 连接服务器:调用connect()方法连接到服务器。
            • 读取数据:使用read()方法从SocketChannel读取数据到ByteBuffer缓冲区。
            • 写入数据:使用write()方法将数据写入SocketChannel。
            • 关闭通道:使用close()方法关闭通道。

              代码:

              try (SocketChannel socketChannel = SocketChannel.open()) {
                  socketChannel.configureBlocking(false);
                  socketChannel.connect(new InetSocketAddress("127.0.0.1", 80));
                  while (!socketChannel.finishConnect()) {
                      // 等待连接完成
                  }
                  ByteBuffer buffer = ByteBuffer.allocate(1024);
                  int bytesRead = 0;
                  while ((bytesRead = socketChannel.read(buffer)) != -1) {
                      buffer.flip();
                      while (buffer.hasRemaining()) {
                          System.out.print((char) buffer.get());
                      }
                      buffer.clear();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
              
              3. ServerSocketChannel

              作用:ServerSocketChannel是用于监听TCP连接请求的通道,它允许服务器监听新进来的TCP连接,并为每个监听到的请求创建一个SocketChannel。

              使用:

              • 获取ServerSocketChannel:使用ServerSocketChannel.open()方法创建ServerSocketChannel。
              • 绑定端口:调用bind()方法绑定到特定端口。
              • 监听连接:使用accept()方法接受新连接请求。

                代码:

                try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
                    serverSocketChannel.socket().bind(new InetSocketAddress(9090));
                    while (true) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println(socketChannel.socket().getRemoteSocketAddress() + "已连接");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                4. DatagramChannel

                作用:DatagramChannel是用于UDP协议的数据读写的通道。

                使用:

                • 获取DatagramChannel:使用DatagramChannel.open()方法创建DatagramChannel。
                • 绑定端口:调用bind()方法绑定到特定端口。
                • 发送数据:使用send()方法发送数据。
                • 接收数据:使用receive()方法接收数据。

                  代码:

                  try (DatagramChannel datagramChannel = DatagramChannel.open()) {
                      datagramChannel.bind(new InetSocketAddress(9001));
                      ByteBuffer buffer = ByteBuffer.allocate(1024);
                      while (true) {
                          buffer.clear();
                          InetSocketAddress address = (InetSocketAddress) datagramChannel.receive(buffer);
                          buffer.flip();
                          System.out.println(address.toString() + "发来的消息" + new String(buffer.array(), 0, buffer.limit()));
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  

                  五、通道与通道之间发送数据

                  通道与通道之间直接发送数据(零拷贝)是Java NIO中的一种优化技术,它允许两个通道之间直接传输数据,避免了数据在用户态和内核态之间的多次拷贝,从而提高了数据传输的效率。

                  在Java NIO中,零拷贝技术主要通过transferTo()和transferFrom()方法实现。这两个方法允许将一个通道中的数据直接传输到另一个通道中,而不需要经过中间的数据拷贝过程。transferTo()方法将文件通道(FileChannel)中的数据传输到套接字通道(SocketChannel)中,transferFrom()方法则相反,将套接字通道中的数据传输到文件通道中。

                  以下是一个使用transferTo()方法的代码,展示了如何使用Java NIO进行零拷贝文件传输:

                  // 创建客户端的SocketChannel
                  SocketChannel socketChannel = SocketChannel.open();
                  // 连接服务器
                  socketChannel.connect(new InetSocketAddress("localhost", 8080));
                  // 创建文件通道
                  FileChannel fileChannel = new RandomAccessFile("source.txt", "r").getChannel();
                  // 使用transferTo()方法将文件通道中的数据传输到套接字通道中
                  long transferred = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
                  // 关闭通道
                  fileChannel.close();
                  socketChannel.close();
                  

                  代码中,transferTo()方法将文件source.txt中的内容直接传输到服务器的套接字通道中,而不需要将数据先拷贝到用户态缓冲区,然后再拷贝到内核态缓冲区,最后再通过网络传输。这种方式显著提高了数据传输的效率,尤其是在处理大文件时,零拷贝技术能够显著减少数据传输的延迟。

                  需要注意的是,零拷贝技术并非适用于所有场景,它主要适用于那些不需要在传输过程中对数据进行修改的情况。如果需要在传输过程中对数据进行修改或加密,则无法完全使用零拷贝技术。另外,零拷贝的实现通常依赖于操作系统底层的系统调用,因此在不同的操作系统和平台上可能略有差异。

                  总结

                  Channel是Java NIO中的一个核心概念,它提供了一种高效、非阻塞的IO操作方式。通过Channel和Buffer的结合使用,提高了IO性能和数据传输效率。在实际应用中,可以根据需求选择不同的Channel实现来进行高效的IO操作。同时,为了更好地利用NIO的特性,需要深入理解其工作原理和与传统的IO差异。


                  关注公众号[码到三十五]获取更多技术干货 !

                  Java NIO 总结: Channel 通道

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]