Netty 基石,Java NIO 核心知识

在深入 Netty 之前,我觉得有必要先对齐一下 Java NIO 的基础知识,因为 Netty 对底层网络 I/O 的操作就是基于 Java NIO 的,所以有必要了解一下。

到时候看源码,会有很多概念,例如 Channel、Selector、SelectionKey、Buffer 等等,这篇我们就来了解下这些名词到底代表着什么,分别是什么意思。

关于 Java NIO 相关的核心,总的来看包含以下三点,分别是:

什么是 Channel

翻译过来就是通道。

我们可以往通道里写数据,也可以从通道里读数据,它是双向的,而与之配套的是 Buffer,也就是你想要往一个通道里写数据,必须要将数据写到一个 Buffer 中,然后写到通道里。

从通道里读数据,必须将通道里的数据先读取到一个 Buffer 中,然后再操作。

在 NIO 中 Channel 有多种类型:

SocketChannel

对标 Socket,我们可以直接将它当做所建立的连接

通过 SocketChannel ,我们可以利用 TCP 协议进行读写网络数据。

ServerSocketChannel

可以对标 ServerSocket,也就是服务端创建的 Socket。

它的作用就是监听新建连的 TCP 连接,为新进一个连接创建对应的 SocketChannel。

之后,通过新建的 SocketChannel 就可以进行网络数据的读写,与对端交互。

可以看到它主要是用来接待新连接,这功能主要就是服务端做的,所以叫ServerSocketChannel。

DatagramChannel

看到 Datagram 应该就知道是 UDP 协议了,是无连接协议。

利用 DatagramChannel 可以直接通过 UDP 进行网络数据的读写。

FileChannel

文件通道,用来进行文件的数据读写。

我们日常开发主要是基于这些 TCP 协议,所以我们把精力放在 SocketChannel 和 ServerSocketChannel 上即可。

我们再回过头来继续看看 SocketChannel 和 ServerSocketChannel。

SocketChannel 主要在两个地方出现:

  1. 客户端,客户端创建一个 SocketChannel 用于连接至远程的服务端。
  2. 服务端,服务端利用 ServerSocketChannel 接收新连接之后,为其创建一个 SocketChannel 。

随后,客户端和服务端就可以通过这两个 SocketChannel 相互发送和接收数据。

ServerSocketChannel 主要出现在一个地方:服务端。

服务端需要绑定一个端口,然后监听新连接的到来,这个活儿就由 ServerSocketChannel 来干。

服务端内常常会利用一个线程,一个死循环,不断地接收新连接的到来。

ServerSocketChannel serverSocketChannel 
  = ServerSocketChannel.open();
 ......
while(true){
  // 接收的新连接
  SocketChannel socketChannel =
      serverSocketChannel.accept();
 .......
}

至此,想必你应该清楚 ServerSocketChannel 和 SocketChannel 的区别和作用了。

Buffer

Buffer 说白了就是内存中可以读写的一块地方,叫缓冲区,用于缓存数据。

其实还真没啥好说的,最多就是讲讲 Java NIO Buffer 的 API。

但讲 API 得太死板了,所以自己上网搜搜吧。我就告知一个结论,这个 API 很不好用,稍微漏写了点,就容易出错 bug,而且还有很多优化的之处,所以 Netty 没用 Java NIO Buffer 而是自己实现了一个 Buffer,叫 ByteBuf。

等我们之后分析 ByteBuf 的时候再来盘一盘。现在你只需要知道 Buffer 主要用来缓存通道的读写数据即可。

对了,看到这可能会有人提出疑问,为什么 Channel 必须和 Buffer 搭配使用?

其实网络数据是面向字节的,但是我们读写的数据往往是多字节的,假设不用 Buffer ,那我们就得一个字节一个字节的调用读和调用写,想想是不是很麻烦?

所以我们搞个 Buffer,把数据拢一拢,这样之后的调用才能更好地处理完整的数据,方便异步的处理等等。

Selector

I/O多路复用的核心玩意。

一个 Selector 上可以注册多个 Channel ,我们从上面得知一个消息 Channel 就对应了一个连接,因此一个 Selector 可以管理多个 Channel 。

具体管理什么?

当任意 Channel 发生读写事件的时候,通过 Selector.select() 就可以捕捉到事件的发生,因此我们利用一个线程,死循环的调用 Selector.select(),这样可以利用一个线程管理多个连接,减少了线程数,减少了线程的上下文切换和节省了线程资源。

这就是 Selector 的核心功能,然后我们再来细说具体是怎样管理的。

首先,创建一个 Selector。

Selector selector = Selector.open();

然后,你需要将被管理的 Channel 注册到 Selector 上,并声明感兴趣的事件。

SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Netty 基石,Java NIO 核心知识

事件一共有以上四种类型,注册的时候可以同时对多种类型的事件感兴趣,例如:

SelectionKey key 
  = channel.register(selector, 
   Selectionkey.OP_READ | SelectionKey.OP_WRITE);

这样,当这个 Channel 发生读或写事件,我们调用 Selector.select()就可以得知有事件发生。

具体 Selector.select() 有三个重载方法:

返回值就是就绪的通道数,一般判断大于 0 即可进行后续的操作。

后续的操作就是调用:

Set selectedKeys = selector.selectedKeys();

获得了一个类型为 Set 的 selectedKeys 集合,那这个 selectedKeys 又是啥玩意?

我们来看一下它的方法和成员:

Netty 基石,Java NIO 核心知识

看到这些成员,其实我们就很清晰了,我们可以通过 selectedKey 得知当前发生的是什么事件,有 isAcceptable、isReadable 等等。

然后还能获得对应的 channel 进行相应的读写操作,还有获取 attachment 等等。

所以得到了 selectedKeys 就可以通过迭代器遍历所有发生事件的连接,然后进行操作。

大致使用的代码如下所示:

while(true) {
 int readyNum = selector.select();
 if (readyNum == 0) {
    continue;
 }
 Set selectedKeys = selector.selectedKeys();
 Iterator keyIterator = selectedKeys.iterator();
 while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove(); //执行完毕之后,需要在循环内移除自己
 }
}

还有个方法就是 Selector.wakeup(),可以唤醒阻塞着的 Selector。

对了还有一点没说,就是如果 Channel 要和 Selector 搭配,那它必须得是非阻塞的,即配置

channel.configureBlocking(false);

从上面的操作,我们可以得知 Selector 处理事件的时候必须快,如果长时间处理某个事件,那么注册到 Selector 上的其他连接的事件就不会被及时处理,造成客户端阻塞。

至此,想必你应该清晰 Selector 具体是如何管理这么多连接的了。

参考:

展开阅读全文

页面更新:2024-05-09

标签:基石   线程   服务端   客户端   通道   核心   协议   发生   事件   操作   知识   数据   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top