java: NIO ,AIO ,BIO
- NIO有三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。Selector可以选择一个通道Channel,通道和Buffer缓冲区交互,客户端和Buffer缓冲区交互。
- BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作, 数据总是从通道读取到缓冲区中,或者从缓冲区写入通道中。
- Selector(选择器) 用于监听多个通道的事件(比如连接请求、数据到达等),因此使用单个线程就可以监听到多个客户端通道。
Channel通道
简介
- NIO channel 通道类似于流,但是有区别:
(1) 通道可以同时读或者写,而流只能读或者只能写
(2)通道可以实现异步读写数据
(3)通道可以从缓冲区读数据,也可以写数据到缓冲区
Channel 在NIO中是一个接口,下面有很多子接口和主要实现。主要实现有:
- FileChannel 注意用于文件的数据读写,
- DatagramChannel 用于UDP的数据读写,
- ServerSocketChannel SocketChannel 用于TCP的数据读写。
ByteBuffer 主要函数
- allocate(int capacity):静态方法,分配一个指定容量的新字节缓冲区。
- clear():清空缓冲区,将位置设置为 0,限制设置为容量,用于准备写入数据。
- flip():将缓冲区从写模式切换为读模式,将限制设置为当前位置,将位置重置为 0,用于准备读取数据。
- rewind():将位置设置为 0,保持限制不变,用于重读缓冲区中的数据。
- compact():将缓冲区中未读取的数据复制到缓冲区的开始位置,将位置设置为复制后的位置,限制设置为容量,用于继续写入数据。
- put(byte b):将一个字节写入缓冲区的当前位置,位置递增。
- put(byte[] src):将字节数组写入缓冲区的当前位置,位置递增。
- put(ByteBuffer src):将另一个字节缓冲区的内容写入当前缓冲区,位置递增。
- get():从缓冲区的当前位置读取一个字节,位置递增。
- get(byte[] dst):从缓冲区的当前位置读取字节到指定的字节数组,位置递增。
- get(ByteBuffer dst):从缓冲区的当前位置读取字节到另一个字节缓冲区,位置递增。
- remaining():返回当前位置与限制之间的字节数量。
- hasRemaining():检查是否还有未读取的字节。
- mark():在当前位置设置一个标记。
- reset():将位置重置为之前设置的标记位置。
- markSupported():检查缓冲区是否支持标记和重置操作。
小结
-
BIO(Blocking I/O):采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少且稳定的场景。
-
NIO(New I/O 或 Non-blocking I/O):使用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,提高性能和可伸缩性,适用于高并发场景。
-
AIO(Asynchronous I/O):采用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,进一步提高了并发处理能力,适用于高吞吐量场景。
- 用CompletionHandler 回调接收事件完成;
java中异步IO 如何获取线程处理的结果呢?
Asynchronous IO : 异步IO;
异步IO的一个常见模型是使用回调(Callback)来处理操作完成的通知和结果。
如:CompletionHandler
在 Linux 下,有三种主要的 I/O 模型:Blocking I/O (BIO),Non-blocking I/O (NIO),和 Asynchronous I/O (AIO)。这些模型代表了不同的方式来处理输入输出操作。
-
Blocking I/O (BIO) :
- 在 Blocking I/O 模型中,当应用程序发起一个 I/O 操作时,操作系统会阻塞应用程序直到操作完成。这意味着应用程序会一直等待数据准备好或者操作完成,无法做其他事情。
- 在 Linux 下,传统的文件 I/O 操作 (
read()和write()) 就是阻塞的。
-
Non-blocking I/O (NIO) :
- Non-blocking I/O 模型允许应用程序在发起 I/O 操作后继续执行其他任务,而不必等待操作完成。
- 在 Linux 下,可以通过设置文件描述符为非阻塞模式,使用
fcntl()函数或ioctl()函数来实现非阻塞 I/O。应用程序可以通过轮询或者使用select()、poll()、epoll()等函数来检查文件描述符的状态,以确定何时可以进行读写操作。
-
Asynchronous I/O (AIO) :
- Asynchronous I/O 模型更进一步,允许应用程序发起 I/O 操作后继续执行其他任务,并在操作完成后得到通知。
- Linux 提供了 AIO 支持,应用程序可以使用
aio_read()和aio_write()等函数来发起异步 I/O 操作,然后可以通过轮询或者回调函数等机制来处理 I/O 完成的通知。
epoll 实现 NIO
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
int main() {
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
return 1;
}
// 添加文件描述符到 epoll 实例中
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = STDIN_FILENO; // 监视标准输入
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
perror("epoll_ctl");
return 1;
}
struct epoll_event events[MAX_EVENTS];
while (1) {
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); // 阻塞等待事件发生
if (num_events == -1) {
perror("epoll_wait");
return 1;
}
for (int i = 0; i < num_events; i++) {
if (events[i].data.fd == STDIN_FILENO) {
char buf[256];
int bytes_read = read(STDIN_FILENO, buf, sizeof(buf));
if (bytes_read == -1) {
perror("read");
return 1;
}
printf("Read from stdin: %.*s\n", bytes_read, buf);
}
}
}
close(epoll_fd);
return 0;
}
aio_read() :的异步回调
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <aio.h>
#include <unistd.h>
#include <errno.h>
void aio_completion_handler(sigval_t sigval) {
struct aiocb *cb = (struct aiocb *)sigval.sival_ptr;
if (aio_error(cb) == 0) {
// 读取操作完成,处理数据
printf("Async read completed successfully. Data read: %s\n", (char *)cb->aio_buf);
} else {
// 读取操作出错
printf("Async read failed with error: %d, %s\n", aio_error(cb), strerror(aio_error(cb)));
}
free(cb->aio_buf);
free(cb);
}
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
struct aiocb *cb = (struct aiocb *)malloc(sizeof(struct aiocb));
memset(cb, 0, sizeof(struct aiocb));
cb->aio_fildes = fd;
cb->aio_buf = malloc(1024); // 分配缓冲区
cb->aio_nbytes = 1024;
cb->aio_sigevent.sigev_notify = SIGEV_THREAD;
cb->aio_sigevent.sigev_notify_function = aio_completion_handler;
cb->aio_sigevent.sigev_notify_attributes = NULL;
cb->aio_sigevent.sigev_value.sival_ptr = cb;
// 发起异步读取操作
if (aio_read(cb) == -1) {
perror("aio_read");
return 1;
}
// 在这里可以继续执行其他操作
// 等待异步操作完成
while (aio_error(cb) == EINPROGRESS) {
// 可以做一些其他工作
}
close(fd);
return 0;
}