6

java的IO与NIO

 2 years ago
source link: http://cbaj.gitee.io/blog/2021/07/04/java%E7%9A%84IO%E4%B8%8ENIO/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

java的IO与NIO

发表于 2021-07-04

| 分类于 java基础

| 4次阅读

字数统计: 1.1k

|

阅读时长 ≈ 4

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

socket通信其实就是阻塞式IO

public void startServer() {
ServerSocket echoServer = null;
int i = 0;
System.out.println("服务器在端口[" + this.port + "]等待客户请求......");
try {
echoServer = new ServerSocket(this.port);
while (true) {
Socket clientRequest = echoServer.accept();
handleRequest(clientRequest, i++);
}
} catch (IOException e) {
System.out.println(e);
}
}

Buffer

一个 Buffer 本质上是内存中的一块, 可以将数据写入这块内存, 从这块内存获取数据

java.nio 定义了以下几个 Buffer 的实现

Java NIO Buffer三大核心概念:position、limit、capacity
最好理解的当然是 capacity,它代表这个缓冲区的容量,一旦设定就不可以更改。比如 capacity 为 1024 的 IntBuffer,代表其一次可以存放 1024 个 int 类型的值。
一旦 Buffer 的容量达到 capacity,需要清空 Buffer,才能重新写入值。

  • 从写操作模式到读操作模式切换的时候(flip),position 都会归零,这样就可以从头开始读写了。
  • 写操作模式下,limit 代表的是最大能写入的数据,这个时候 limit 等于 capacity。
  • 写结束后,切换到读模式,此时的 limit 等于 Buffer 中实际的数据大小,因为 Buffer 不一定被写满了。

数据发送流程

channel

所有的 NIO 操作始于通道,通道是数据来源或数据写入的目的地,主要地, java.nio 包中主要实现的以下几个 Channel:

  • FileChannel:文件通道,用于文件的读和写
  • DatagramChannel:用于 UDP 连接的接收和发送
  • SocketChannel:把它理解为 TCP 连接通道,简单理解就是 TCP 客户端
  • ServerSocketChannel:TCP 对应的服务端,用于监听某个端口进来的请求

Selector

Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接

java.nio.channels.Selector
支持IO多路复用的抽象实体
注册Seletable Channel
SelectionKey —— 表示Selector和被注册的channel之间关系,一份凭证
SelectionKey 保存channel感兴趣的事件
Selector.select 更新所有就绪的SelectionKey的状态,并返回就绪的channel个数
迭代Selected Key集合并处理就绪channel

创建selector(create a selector)

Selector selector = Selector.open();

注册channel到seletor上

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

register的第二个参数,这个参数是一个“关注集合”,代表关注的channel状态,
有四种基础类型可供监听, 用SelectionKey中的常量表示如下:

SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

从Selector中选择channel(Selecting Channels via a Selector)

一旦向Selector注册了一个或多个channel后,就可以调用select来获取channel
select方法会返回所有处于就绪状态的channel
select方法具体如下:

int select()
int select(long timeout)
int selectNow()

select()方法的返回值是一个int,代表有多少channel处于就绪了。也就是自上一次select后有多少channel进入就绪。

selectedKeys()
在调用select并返回了有channel就绪之后,可以通过选中的key集合来获取channel,这个操作通过调用selectedKeys()方法:

Set<SelectionKey> selectedKeys = selector.selectedKeys();
Set<SelectionKey> selectedKeys = selector.selectedKeys();

Iterator<SelectionKey> 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();
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK