回顾
上一章我们介绍了操作系统层面的 IO 模型.
阻塞 IO 模型.
非阻塞 IO 模型.
IO 复用模型.
信号驱动 IO 模型(用的不多, 知道个概念就行).
异步 IO 模型.
并且介绍了 IO 多路复用的底层实现中, select,poll 和 epoll 的区别.
几个概念
我们在这里在强调一下几个概念.
一个 IO 操作的具体步骤:
对于操作系统来说, 进程是没有直接操作硬件的权限的, 所以必须请求内核来帮忙完成.
等待数据准备好, 对于一个套接字上得操作, 这一步骤关系到数据从网络到达, 并将其复制到内核某个缓冲区.
将数据从内核缓冲区复制到进程缓冲区.
同步和异步的区别在于第二个步骤是否阻塞, 如果从内核缓冲区复制到用户缓冲区的过程阻塞, 那么就是同步 IO, 否则就是异步 IO. 所以上面提到的前四种 IO 模型都是同步 IO, 最后一种是异步 IO.
阻塞和非阻塞的区别在于第一步, 发起 IO 请求是否会被阻塞, 如果阻塞直到完成那么就是传统的阻塞 IO, 否则就是非阻塞 IO. 所以上面提到的第一种 IO 模型是阻塞 IO, 其余的都是非阻塞 IO.
Java IO API
介绍完操作系统层面的 IO 模型, 我们来看看, Java 提供的 IO 相关的 API.
Java 中提供三种 IO 操作的 API, 阻塞 IO(BIO, 同步阻塞), 非阻塞 IO(NIO, 同步非阻塞)和异步 IO (AIO, 异步非阻塞).
Java 中提供的 IO 有关的 API, 在文件处理的时候, 其实是依赖操作系统层面的 IO 操作实现的. 比如在 Linux 2.6 以后, Java 中的 NIO 和 AIO 都是通过 epoll(前面讲过的, IO 多路复用) 来实现的. 而在 Windows 上, AIO 是通过 IOCP 来实现的.
可以把 Java 中的 BIO,NIO 和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装. 程序员在使用这些 API 的时候, 不需要关心操作系统层面的知识, 只需要使用 Java API 就可以了.
- // 服务端
- ServerSocket serverSocket = ......
- serverSocket.bind(8899);
- while(true){
- Socket sokcet = serverSocket.accept(); // 阻塞方法
- new Thread(socket);
- run(){
- socket.getInputStream();
- ....
- ....
- }
- }
- // 客户端
- Socket socket = new Socket("localhost",8899);
- socket.connect();
// NIO 多路复用 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); threadPool.execute(new Runnable() { @Override public void run() { try (Selector selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) { serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select(); // 阻塞等待就绪的 Channel Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); try (SocketChannel channel = ((ServerSocketChannel) key.channel()).accept()) { channel.write(Charset.defaultCharset().encode("你好, 世界")); } iterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } } }); // Socket 客户端(接收信息并打印) try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream())); bufferedReader.lines().forEach(s -> System.out.println("NIO 客户端:" + s)); } catch (IOException e) { e.printStackTrace(); }
// AIO 线程复用版 Thread sThread = new Thread(new Runnable() { @Override public void run() { AsynchronousChannelGroup group = null; try { group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4)); AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); server.accept(null, new CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>() { @Override public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) { server.accept(null, this); // 接收下一个请求 try { Future<Integer> f = result.write(Charset.defaultCharset().encode("你好, 世界")); f.get(); System.out.println("服务端发送时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); result.close(); } catch (InterruptedException | ExecutionException | IOException e) { e.printStackTrace(); } } @Override public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) { } }); group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }); sThread.start(); // Socket 客户端 AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); Future<Void> future = client.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)); future.get(); ByteBuffer buffer = ByteBuffer.allocate(100); client.read(buffer, null, new CompletionHandler<Integer, Void>() { @Override public void completed(Integer result, Void attachment) { System.out.println("客户端打印:" + new String(buffer.array())); } @Override public void failed(Throwable exc, Void attachment) { exc.printStackTrace(); try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }); Thread.sleep(10 * 1000);
File.read(file, buf, len); Socket.send(socket, buf, len);
File file = new File("test.zip"); RandomAccessFile raf = new RandomAccessFile(file, "rw"); FileChannel fileChannel = raf.getChannel(); SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("", 1234)); // 直接使用了 transferTo()进行通道间的数据传输 fileChannel.transferTo(0, fileChannel.size(), socketChannel);
File file = new File("test.zip"); RandomAccessFile raf = new RandomAccessFile(file, "rw"); FileChannel fileChannel = raf.getChannel(); MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
来源: https://www.cnblogs.com/paulwang92115/p/12199045.html