Netty入门指南之NIO Channel详解

编程入门 行业动态 更新时间:2024-10-28 03:29:46

Netty入门指南之NIO Channel<a href=https://www.elefans.com/category/jswz/34/1770044.html style=详解"/>

Netty入门指南之NIO Channel详解

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Netty应用专栏_Aomsir的博客-CSDN博客

文章目录

  • 参考文献
  • 前言
  • Channel
    • 简介
    • 常见Channel
    • Channel获取方式
  • Buffer简介
  • 演示案例
    • IO流
    • Channel
      • 错误案例
      • 改进案例
      • 注意
  • 总结

参考文献

  • 孙哥suns说Netty
  • Netty官方文档

前言

从本篇文章开始,我们将深入学习 NIO(非阻塞I/O)编程的相关内容,从头到尾详尽分析,包括Selector和Reactor模型,这将为我们后续学习Netty等更高层次的网络编程库打下坚实的基础。NIO编程的核心元素主要包括ChannelBuffer

NIO编程使用Channel来进行通信。在服务端,我们还引入了Selector选择器,它帮助我们主动监控客户端的Channel,确保这些Channel能够正常通信(即正常连接且没有阻塞)。通过监控客户端的请求链路,Selector的作用是,一旦发现某个客户端阻塞,它可以将分配给该客户端的线程重新分配给其他可用客户端,这样一个线程就能为多个客户端提供服务。需要注意的是,每个客户端都拥有自己独立的Channel,不共享一个Channel。这篇文章将深入学习和理解Channel的概念,为后续的内容打下坚实的基础。

Channel

简介

Channel是一种用于IO通信的管道,类似于传统的InputStreamOutputStream。然而,与传统IO流不同,其中有输入流和输出流的方向性,NIO中的Channel是 无方向性。在传统IO开发中,为了读取文件并在JVM中进行操作后将结果写回文件,我们需要一个InputStream输入流将文件读入JVM,然后使用OutputStream输出流将结果写回文件。这些流是单向的,每个用于特定的目的。而在NIO中,Channel既可以用于读取数据,也可以用于写入数据,这为通用的双向数据传输提供了更大的灵活性

常见Channel

  • 文件IO操作
    • FileChannel:读和写文件中的数据
  • 网络IO操作
    • ServerSocketChannel:监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接创建一个SocketChannel
    • SocketChannel:通过TCP读写网络中的数据
    • DatagramChannel:通过UDP读写网络中的数据

Channel获取方式

  • 文件IO操作
    • FileInputStream/FileOutputStream获取
    • RandonAccessFile获取
  • 网络通信
    • Socket获取
    • ServerSocket获取
    • DatagramSocket获取
FileChannel channel = new FileInputStream(TestNIO1.class.getClassLoader().getResource("data.txt").getFile()).getChannel();FileChannel channel = new RandomAccessFile("data.txt", "rw").getChannel();

Buffer简介

因在下一篇文章中,我们将详细讲解Buffer,因为它是NIO中的一个非常核心的概念。掌握好Buffer对于理解后续的内容,包括Netty,至关重要。

Buffer可以被看作是JVM内存中的一块区域,它类似于一个缓冲区。在传统的流通信中,我们通常使用字节数组来装载接收到的数据。Buffer也类似于这个字节数组,但不同之处在于它具有读写指针,用于标识内存中的数据是用于读取还是写入。这一特性使得Buffer非常适用于NIO中的数据处理

演示案例

IO流

如下是使用Stream流的方式的进行读操作

public class TestNIO1 {private static final Logger log = LoggerFactory.getLogger(TestNIO1.class);public static void main(String[] args) throws IOException {// 创建输入流InputStream inputStream = TestNIO1.class.getClassLoader().getResourceAsStream("data.txt");// 创建缓冲区byte[] bytes = new byte[1024];// 读取数据到缓冲区while (inputStream.read(bytes) != -1){String s = new String(bytes);log.info("s = {}", s);}}
}

Channel

错误案例

在下面的案例中,我们设置Buffer的大小为10个字节。但是,如果我们尝试读取一个13字节的数据流,那么后面的3个字节将一直保留在缓冲区中,无法读取。动态调整Buffer的大小可能不是一个明智的选择,因为这会引入复杂性。

为了解决这个问题,我们可以采用改进案例中的方法。不再固定Buffer的大小,而是使用循环不断读取,直到所有数据被消耗。这种方式可以有效地处理不定长度的数据,而不需要频繁地调整缓冲区大小

public class TestNIO1 {private static final Logger log = LoggerFactory.getLogger(TestNIO1.class);public static void main(String[] args) throws IOException {// 1.创建Channel通道 - FileChannelFileChannel channel = new FileInputStream("/Users/aomsir/MyStudyProject/Java/Netty/netty-basic-01/data.txt").getChannel();// 2.创建Buffer缓冲区,容量为10字节,具体根据文件编码来定ByteBuffer buffer = ByteBuffer.allocate(10);while (true) {// 3.把channel读取的数据放入buffer,读完以后返回的是-1int read = channel.read(buffer);if (-1 == read) {break;}// 4.设置buffer为读模式,代表程序从buffer中读取数据buffer.flip();// 5.循环读取缓冲区数据while (buffer.hasRemaining()) {byte b = buffer.get();System.out.println((char) b);}// 6.操作之后将buffer设置为写模式buffer.clear();}}
}

改进案例

改进案例非常简单,我们可以通过循环复用Buffer来处理未读取的数据。这意味着我们不需要不断调整Buffer的大小,而是在一个循环中不断读取数据,直到所有数据都被处理。这种方法能够有效地解决数据流长度不确定的情况,确保不会漏掉任何数据。同时上一个错误案例没有做异常处理,此改进版本做了异常处理。

public class TestNIO2 {private static final Logger log = LoggerFactory.getLogger(TestNIO2.class);public static void main(String[] args) {FileChannel channel = null;try {// 1.通过FileInputStream获取对应的FileChannel管道channel = new FileInputStream(TestNIO1.class.getClassLoader().getResource("data.txt").getFile()).getChannel();// 2.创建Buffer缓冲区,容量为10字节,具体根据文件编码来定ByteBuffer buffer = ByteBuffer.allocate(10);while (true) {// 3.让buffer从channel中读取数据,如果没有读到数据则返回-1int read = channel.read(buffer);if (-1 == read) {break;}// 4.设置buffer为读模式,代表程序从buffer中读取数据buffer.flip();// 5.循环读取缓冲区数据while (buffer.hasRemaining()) {byte b = buffer.get();log.info("(char)b = {}", (char)b);}// 6.操作之后将buffer设置为写模式,方便下一次写入数据buffer.clear();}} catch (Exception e) {e.printStackTrace();} finally {if (channel != null) {try {channel.close();} catch (IOException e) {throw new RuntimeException();}}}}
}

注意

在Buffer中,通过使用flip()方法,我们可以切换为读操作的模式。这表示其他程序可以从Buffer中读取数据。另一方面,要切换为写操作模式,我们可以使用clear()方法。这表示后续的程序可以向Buffer中写入数据,或者从Channel中读取数据并放入Buffer中进行进一步处理。

总结

在本篇文章中,我们将深入研究NIO中的Channel,探讨其双向可读可写的特性,介绍一些常见的Channel类型以及它们的创建方式。我们还将详细演示FileChannel的使用,而在后续的内容中,我们将逐渐学习SocketChannel、ServerSocketChannel等更多内容。这些知识将有助于我们更好地理解和充分利用NIO中的通道,为后续的学习和应用奠定坚实的基础。

更多推荐

Netty入门指南之NIO Channel详解

本文发布于:2023-11-16 00:37:08,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1610800.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:详解   入门   指南   Netty   Channel

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!