java中的IO类型
- BIO 同步堵塞IO
- NIO 同步非堵塞IO
- AIO 异步非堵塞IO
同步异步,堵塞非堵塞
在一个网络请求中,客户端发送请求到服务端
- 客户端发送请求后,客户端发送请求后,等待服务端响应,客户端:堵塞 请求:同步
- 客户端发送请求后,就去干别的事了,时不时来检查服务端有没有响应。客户端:非堵塞 请求:同步
- 客户端发送请求后,就去干别的事了,不需要检查服务端响应,等服务端响应后再来处理业务逻辑。客户端:非堵塞 请求:异步
Java中的NIO
与 Socket 类和 ServerSocket 类对应,NIO 也提供了 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,在 JDK1.4 中引入。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式非常简单,但性能和可靠性都不好,非阻塞模式正好相反。我们可以根据自己的需求来选择合适的模式,一般来说,低负载、低并发的应用程序可以选择同步阻塞 IO 以降低编程复杂度,但是对于高负载、高并发的网络应用,需要使用 NIO 的非阻塞模式进行开发。
- (1)缓冲区 Buffer
Buffer 是一个对象,它包含一些要写入或者要读出的数据,在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的;在写入数据时,写入到缓冲区中,任何时候访问 NIO 中的数据,都是通过缓冲区进行操作。
缓冲区实质上是一个数组。通常它是一个字节数组(ByteBuffer),也可以使用其他种类的数组,但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。常用的有ByteBuffer,其它还有CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。
- (2)通道 Channel
Channel 是一个通道,可以通过它读取和写入数据,它就像自来水管一样,网络数据通过 Channel 读取和写入。通道与流的不同之处在于通道是双向的,流只是一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而且通道可以用于读、写或者用于读写。同时Channel 是全双工的,因此它可以比流更好的映射底层操作系统的API。特别是在Unix网络编程中,底层操作系统的通道都是全双工的,同时支持读写操作。我们常用到的 ServerSocketChannnel 和 SocketChannel 都是SelectableChannel 的子类。
- (3)多路复用器Selector
多路复用器 Selector 是 Java NIO 编程的基础,多路复用器提供选择已经就绪的任务的能力,简单的说,Selector 会不断的轮询注册在其上的 Channel,如果某个 Channel 上面有新的 TCP 连接接入、读和写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以获取就绪 Channel 的集合,进行后续的 I/O 操作。
一个多用复用器 Selector 可以同时轮询多个 Channel,由于 JDK 使用了 epoll() 代替传统的 select() 实现,所以它并没有最大连接句柄 1024/2048 的限制,这也意味着只需要一个线程负责 Selector 的轮询,就可以接入成千上万的客户端。
尽管 NIO 编程难度确实比同步阻塞 BIO 大很多,但是我们要考虑到它的优点:
(1)客户端发起的连接操作是异步的,可以通过在多路复用器注册 OP_CONNECT 等后续结果,不需要像之前的客户端那样被同步阻塞。
(2)SocketChannel 的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样IO通信线程就可以处理其它的链路,不需要同步等待这个链路可用。
(3)线程模型的优化:由于 JDK 的 Selector 在 Linux 等主流操作系统上通过 epoll 实现,它没有连接句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个 Selector 线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降,因此,它非常适合做高性能、高负载的网络服务器。
IO demo
BIO
public class ThreadPoolBIODemo {
public static void main(String[] args) throws IOException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("0.0.0.0", 8888), 50);
Socket socket;
while ((socket = serverSocket.accept()) != null) {
final Socket clientSocket = socket;
executorService.submit(new Runnable() {
@Override
public void run() {
try {
InputStream is = clientSocket.getInputStream();
byte[] data = new byte[1024];
is.read(data);
OutputStream out = clientSocket.getOutputStream();
out.write(data);
clientSocket.close();
} catch (Exception e) {
;
}
}
});
}
}
}
NIO
public class NIODemo {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress("0.0.0.0", 8888), 50);
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
ByteBuffer buffer = ByteBuffer.wrap(new byte[1024]);
SocketChannel clientChannel = (SocketChannel) key.channel();
int read = clientChannel.read(buffer);
if (read == -1) {
key.cancel();
clientChannel.close();
} else {
buffer.flip();
clientChannel.write(buffer);
}
}
}
iterator.remove();
}
}
}
AIO
服务端
public class AIO_Server {
static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) throws InterruptedException {
int port = 7890;
new Thread(new AioServer(port)).start();
TimeUnit.MINUTES.sleep(60);
}
static class AioServer implements Runnable {
int port;
AsynchronousChannelGroup group;
AsynchronousServerSocketChannel serverSocketChannel;
public AioServer(int port) {
this.port = port;
init();
}
public void init() {
try {
// 创建处理线程池
group = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 5);
// 创建服务channel
serverSocketChannel = AsynchronousServerSocketChannel.open(group);
// 丙丁端口
serverSocketChannel.bind(new InetSocketAddress(port));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// 接收请求
// accept的第一个参数附件,第二个参数是收到请求后的接收处理器
// 接收处理器AcceptHandler泛型的第一个参数的处理结果,这里是AsynchronousSocketChannel,即接收到的请求的channel
// 第二个参数是附件,这里是AioServer,即其实例
serverSocketChannel.accept(this, new AcceptHandler());
}
}
/**
* 接收请求处理器
* completionHandler泛型的第一个参数的处理结果,这里是AsynchronousSocketChannel,即接收到的请求的channel,
* 第二个参数是附件,这里是AioServer,即其实例
*/
static class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AioServer> {
@Override
public void completed(AsynchronousSocketChannel result, AioServer attachment) {
// 继续接收下一个请求,构成循环调用
attachment.serverSocketChannel.accept(attachment, this);
try {
System.out.println("接收到连接请求:" + result.getRemoteAddress().toString());
// 定义数据读取缓存
ByteBuffer buffer = ByteBuffer.wrap(new byte[1024]);
// 读取数据,并传入数据到达时的处理器
// read的第一个参数数据读取到目标缓存,第二个参数是附件,第三个传输的读取结束后的处理器
// 读取处理器泛型的第一个参数是读取的字节数,第二个参数输附件对象
result.read(buffer, buffer, new ReadHandler(result));
// 新开新城发送数据
new Thread(new WriteThread(result)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, AioServer attachment) {
}
}
/**
* 读取数据处理器
* completionHandler第一个参数是读取的字节数,第二个参数输附件对象
*/
static class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
AsynchronousSocketChannel socketChannel;
public ReadHandler(AsynchronousSocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result == -1) {
attachment.clear();
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
attachment.flip();
String readMsg = charset.decode(attachment).toString();
System.out.println("服务端接收到的数据:" + readMsg);
attachment.compact();
// 继续接收数据,构成循环
socketChannel.read(attachment, attachment, this);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
}
/**
* 写出数据处理器
*/
static class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
AsynchronousSocketChannel socketChannel;
Scanner scanner;
public WriteHandler(AsynchronousSocketChannel socketChannel, Scanner scanner) {
this.socketChannel = socketChannel;
this.scanner = scanner;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.compact();
String msg = scanner.nextLine();
System.out.println("服务端即将发送的数据:" + msg);
attachment.put(charset.encode(msg));
attachment.flip();
// 继续写数据,构成循环
socketChannel.write(attachment, attachment, this);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
}
static class WriteThread implements Runnable {
private AsynchronousSocketChannel channel;
public WriteThread(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void run() {
// 第一缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
String msg = scanner.nextLine();
System.out.println("服务端输入数据:" + msg);
buffer.put(charset.encode(msg + System.lineSeparator()));
buffer.flip();
// 写入数据,并有写数据时的处理器
// write的第一个参数是数据写入的缓存,第二个参数是附件,第三个参数写结束后的处理器
// 读取处理器泛型的第一个参数是写入的字节数,第二个是附件类型
channel.write(buffer, buffer, new WriteHandler(channel, scanner));
}
}
}
客户端
public class AIO_Client {
static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) throws InterruptedException {
int port = 7890;
String host = "127.0.0.1";
// 启动客户端
new Thread(new AIOClient(port, host)).start();
TimeUnit.MINUTES.sleep(100);
}
static class AIOClient implements Runnable {
int port;
String host;
AsynchronousChannelGroup group;
AsynchronousSocketChannel channel;
InetSocketAddress address;
public AIOClient(int port, String host) {
this.port = port;
this.host = host;
// 初始化
init();
}
private void init() {
try {
// 创建处理线程组
group = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 5);
// 创建客户端channel
channel = AsynchronousSocketChannel.open(group);
address = new InetSocketAddress(host, port);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// 接收请求,并传入收到请求后的处理器
// connect 方法的第一二个参数是目标地址,第二个参数是附件对象,第三个参数是连接处理器
// 连接处理器的泛型的第一个参数为空(即Void),第二个参数为附件
channel.connect(address, channel, new ConnectHandler());
}
}
/**
* 连接处理器
*/
static class ConnectHandler implements CompletionHandler<Void, AsynchronousSocketChannel> {
@Override
public void completed(Void result, AsynchronousSocketChannel attachment) {
try {
System.out.println("connect server: " + attachment.getRemoteAddress().toString());
// 定义数据读取缓存
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据,并传入到数据到达时的处理器
attachment.read(buffer, buffer, new ReadHandler(attachment));
// 新开线程,发送数据
new WriteThread(attachment).start();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
}
}
/**
* 读处理器
*/
static class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
AsynchronousSocketChannel channel;
public ReadHandler(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
String readMsg = charset.decode(attachment).toString();
System.out.println("client receive msg: " + readMsg);
attachment.compact();
// 继续接收数据,构成循坏
channel.read(attachment, attachment, this);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
}
/**
* 写处理器
*/
static class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
AsynchronousSocketChannel channel;
Scanner scanner;
public WriteHandler(AsynchronousSocketChannel channel, Scanner scanner) {
this.channel = channel;
this.scanner = scanner;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.compact();
System.out.print("client input data: ");
String msg = scanner.nextLine();
System.out.println("clinet will send msg:" + msg);
attachment.put(charset.encode(msg));
attachment.flip();
// 继续写入数据,构成循环
channel.write(attachment, attachment, this);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
}
/**
* 写处理独立创建线程
*/
static class WriteThread extends Thread {
private AsynchronousSocketChannel channel;
public WriteThread(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
System.out.print("client input data:");
String msg = scanner.nextLine();
System.out.println("client send msg:" + msg);
buffer.put(charset.encode(msg));
buffer.flip();
channel.write(buffer, buffer, new WriteHandler(channel, scanner));
}
}
}