标签:chm ini address ring color attach 两种 类型 返回
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。
了解NIO前先熟悉几个概念
1)阻塞(Block)和非阻塞(Non-Block):
阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪的一种处理方式。
阻塞:需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里。
非阻塞:当进程访问数据缓冲区的时候,如果数据没有准备好则直接返回,不会等待。如果数据已经准备好,也直接返回。
2)同步(Synchronization)和异步(Asynchronous)的方式:
同步和异步都是基于应用程序和操作系统处理IO 事件所采用的方式。
异步:所有的IO 读写交给操作系统去处理,应用程序只需要等待通知。
同步: 处理IO 事件的时候,必须阻塞在某个方法上面等待我们的IO 事件完成。
所以对于对于标准的IO属于同步阻塞方式,NIO属于同步非阻塞模式,AIO属于异步非阻塞模式。
在java NIO由几个核心部门组成:缓存Buffers;通道Channels;选择器Selectors。
一、缓存Buffers
缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO 库中,所有数据都是用缓冲区处理的。
使用Buffer读写数据一般遵循以下四个步骤:
1)写入数据到Buffer: 直接将数据写入buffer
2)调用flip()方法:通过flip()将buffer从写模式切换到读模式
3)从Buffer中读取数据:
4)调用clear()方法或者compact()方法:
读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
在NIO 中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer 类型与之相对应,它们之间的继承关系如下图所示:
在缓冲区中,最重要的属性有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪:
position:指定了下一个将要被写入或者读取的元素索引,它的值由get()/put()方法自动更新,在新创建一个Buffer 对象时,position 被初始化为0。
limit:指定还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
capacity:指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用的底层数组的容量。
二、通道Channels
通道是一个对象,通过它可以读取和写入数据,但所有数据的读写都先通过Buffer对象来处理再到通道中。
Java NIO中几个最重要的通道的实现
FileChannel:从文件中读写数据
DatagramChannel:能通过UDP读写网络中的数据
SocketChannel:能通过TCP读写网络中的数据
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel
他们之间的继承关系如下:
1)通过通道读取过程:
- 从FileInputStream 获取Channel
- 创建Buffer
- 将数据从Channel 读取到Buffer中
public class FileInputProgram { static public void main( String args[] ) throws Exception { FileInputStream fin = new FileInputStream("c:\\test.txt"); // 获取通道 FileChannel fc = fin.getChannel(); // 创建缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); // 读取数据到缓冲区 fc.read(buffer); buffer.flip(); while (buffer.remaining()>0) { byte b = buffer.get(); System.out.print(((char)b)); } fin.close(); }
2)使用NIO 写入数据
可以分为下面三个步骤:
- 从FileInputStream 获取Channel
- 创建缓存Buffer
- 将数据从Channel写入到Buffer中
public class FileOutputProgram { static private final byte message [] = { 83, 111, 109, 101, 32, 98, 121, 116, 101, 115, 46 }; static public void main(String args[]) throws Exception { FileOutputStream fout = new FileOutputStream("e:\\test.txt"); //新建渠道 FileChannel fc = fout.getChannel(); //新建buffer ByteBuffer buffer = ByteBuffer.allocate(1024); for (int i = 0; i < message.length; ++ i) { buffer.put(message[i]); } buffer.flip(); //写入渠道 fc.write(buffer); fout.close(); } }
三、selector选择器
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
使用NIO 中非阻塞I/O 编写服务器处理程序,大体上可以分为下面三个步骤:
1)向Selector 对象注册感兴趣的事件
2)从Selector 中获取感兴趣的事件
3)根据不同的事件进行相应的处理
注册事件:
如果想要同时注册几个事件时,可以通过“位或”操作符将常量连接起来,比如:SelectionKey.OP_READ | SelectionKey.OP_WRITE
/* * 向selector注册事件 * 注册的事件包含四种: * SelectionKey.OP_CONNECT * SelectionKey.OP_ACCEPT * SelectionKey.OP_READ * SelectionKey.OP_WRITE * */ private Selector getSelector() throws IOException { // 创建Selector 对象 Selector sel = Selector.open(); // 创建可选择通道,并配置为非阻塞模式 ServerSocketChannel server = ServerSocketChannel.open(); server.configureBlocking(false); // 绑定通道到指定端口 ServerSocket socket = server.socket(); InetSocketAddress address = new InetSocketAddress(port); socket.bind(address); // 向Selector 中注册感兴趣的事件 server.register(sel, SelectionKey.OP_ACCEPT); return sel; }
获取感兴趣的事件:
/* * 开始监听 * */ public void listen() { System.out.println("listen on " + port); try { while(true) { // 该调用会阻塞,直到至少有一个事件发生 selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> iter = keys.iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); //最终处理事件 process(key); } } } catch (IOException e) { e.printStackTrace(); } }
对事件进行处理,可以针对不同的事件进行不同的处理流程:
/* * 根据不同的事件做处理 * */ private void process(SelectionKey key) throws IOException{ // 接收请求 if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); } // 读信息 else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); int len = channel.read(buffer); if (len > 0) { buffer.flip(); name = new String(buffer.array(),0,len); SelectionKey sKey = channel.register(selector, SelectionKey.OP_WRITE); sKey.attach(name); } else { channel.close(); } buffer.clear(); } // 写事件 else if (key.isWritable()) { SocketChannel channel = (SocketChannel) key.channel(); String name = (String) key.attachment(); ByteBuffer block = ByteBuffer.wrap(("Hello " + name).getBytes()); if(block != null){ channel.write(block); } else{ channel.close(); } } }
标签:chm ini address ring color attach 两种 类型 返回
原文地址:https://www.cnblogs.com/kma-3/p/9625443.html