BIO即为阻塞IO,在网络编程中,它会在建立连接和等待连接的对端准备数据阶段进行阻塞。因此为了支撑高并发的用户访问,一般会为每一个socket 连接分配一个线程。但使用的瓶颈更加明显,无法支持上百万、甚至千万以上的并发。且线程切换带来的开销也更大。
代码示例:
NIO 即非阻塞IO,是JDK 1.4 更新的api, 核心内容是 将建立连接、数据可读、可写等事件交给了操作系统来维护, 通过调用操作系统的 api (如:select、epoll等),来判断当前是否支持:可读、可写,如果当前不可操作,那么直接返回,从而实现了非阻塞。 而不需要像 BIO 那样每次去轮询等待连接的建立以及数据的准备是否完成。主要核心的模块分以下几类:
一个特定基类(byte、short、int、long 等)的数据容器,用作在建立socket 连接之后的数据传输。 通过 capacity, limit, position,mark 指针来实现数据的读写
get()、put() 方法为每个子类都具有的读、写数据的api方法,当从当前的 position 读或写的同时,position会增加 相应读写的数据的长度。当position 达到limit 之后,再次 get、put则会抛出异常
一个 channel 代表一个与“实体”的连接通道,如:硬件设备、文件、网络 socket 。通过连接通道可以使得客户端-服务器互相传输数据,因此通道也是全双工的(因为是建立在TCP 传输层的协议上,因此具备全双工的能力)。
JDK 中 channel 可以分为以下几类:
SelectableChannel 用于 阻塞和非阻塞 socket 连接的通道FileChannel 用于文件操作,包括:reading, writing, mapping, and manipulating a file用于 SelectableChannel 的多路复用器,当使用非阻塞的 socket 时,需要将监听的通道 SelectableChannel 感兴趣的事件注册到 selector 多路复用器上(selector 实际上是通过调用操作系统层面的 select、epoll 方法来获取当前可用的时间)
与之对应的感兴趣的事件用 SelectionKey 来表示
OP_READ = 1 << 0; 可读OP_WRITE = 1 << 2; 可写OP_CONNECT = 1 << 3; // 完成连接OP_ACCEPT = 1 << 4; // 接收连接处理流程图:
代码示例:
通过 ServerSocketChannel 监听 8082 端口设置为非阻塞选择与操作系统适配的选择器,serverSocketChannel 的 OP_ACCEPT 事件注册到 selector 选择器上当OP_ACCEPT 事件触发时,将所有建立好的Socketchannel 连接的感兴趣的事件(这里为 read事件)再次注册到Selector 上 // 1.根据操作系统选择适当的底层 io复用方法 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8082)); //2.设置为非阻塞 serverSocketChannel.configureBlocking(false); //3.选择与操作系统适配的选择器 Selector selector = Selector.open(); //将 serverSocket 的OP_ACCEPT 事件注册到 selector 选择器上 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // 4.监听当前连接建立情况 int select = selector.select(); if (select > 0) { //判断连接业务类型 Set<SelectionKey> set = selector.selectedKeys(); Iterator<SelectionKey> iterator = set.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); //建立连接 if (key.isAcceptable()) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //通过 accept 方法获取与 server端 已经创建好的 socket连接 SocketChannel sc = ssc.accept(); //设置为非阻塞 sc.configureBlocking(false); //注册感兴趣的事件为 READ sc.register(selector, SelectionKey.OP_READ); } //可读 else if (key.isReadable()) { SocketChannel socket = (SocketChannel) key.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); socket.read(byteBuffer); System.out.println(new String(byteBuffer.array(), StandardCharsets.UTF_8)); key.interestOps(SelectionKey.OP_WRITE); } //可写 else if (key.isWritable()) { SocketChannel socket = (SocketChannel) key.channel(); socket.write(ByteBuffer.wrap("I'm receive your message".getBytes(StandardCharsets.UTF_8))); socket.close(); System.out.println("连接关闭成功!"); } } } }NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。 异步的套接字通道时真正的异步非阻塞I/O,对应于UNIX网络编程中的事件驱动I/O(AIO)。他不需要过多的Selector对注册的通道进行轮询即可实现异步读写,从而简化了NIO的编程模型。
代码示例
private static void server() throws IOException { //根据操作系统建立对应的底层操作类 AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open(); channel.bind(new InetSocketAddress(8082)); while (true) { Future<AsynchronousSocketChannel> future = channel.accept(); try { AsynchronousSocketChannel asc = future.get(); System.out.println("建立连接成功"); Future<Integer> write = asc.write(ByteBuffer.wrap("Now let's exchange datas".getBytes(StandardCharsets.UTF_8))); while (!write.isDone()) { TimeUnit.SECONDS.sleep(2); } System.out.println("发送数据完成"); asc.close(); } catch (Exception e) { e.printStackTrace(); } } } private static void client() throws Exception { AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open(); Future<Void> future = socketChannel.connect(new InetSocketAddress(8082)); while (!future.isDone()) { TimeUnit.SECONDS.sleep(2); } ByteBuffer buffer = ByteBuffer.allocate(1024); Future<Integer> read = socketChannel.read(buffer); while (!read.isDone()) { TimeUnit.SECONDS.sleep(2); } System.out.println("接收服务器数据:" + new String(buffer.array(), 0, read.get())); }转载于:https://www.cnblogs.com/coding400/p/11395185.html
相关资源:bio nio aio demo