Java nio 学习笔记(二) Charset(字符集)与Selector(异步IO)的知识

 三.Charset(字符集)

在java.nio.charset包中共提供了Charset、CharsetDecoder、CharsetEncoder、CodeResult、CodingErrorAction五个类,均继承自Object类,其中Charset实现了Comparable接口,其它类均为自身实现。

Java中的字符使用unicode编码,每个字符占用两个字节。字节码本身只是一些数字,放在正确的上下文中可以被正确的解析。向ByteBuffer中存放数据时需要考虑字符集的编码方式,从中读取时需要考虑字符集的解码。

要读和写文本需要分别使用CharsetDecoder(解码器)和CharsetEncoder(编码器)。

编码:百科中这样定义,编码(coding)是在一个主题或单元上为数据存储,管理和分析的目的而转换信息为编码值(典型地如数字)的过程。在密码学中,编码是指在编码或密码中写的行为。n位二进制数可以组合成2的n次方个不同的信息,给每个信息规定一个具体码组,这种过程也叫编码。数字系统中常用的编码有两类,一类是二进制编码,另一类是二—十进制编码。

(1)如何得到一个CharSet

在JDK源码中提供两种方式得到一个CharSet实例:

       CharSet cs = CharSet.forName(“编码方式”);

       CharSet cs = CharSet.defaultCharSet();

第一种方法返回一个指定字符格式的CharSet,第二种方法返回当前虚拟机默认的字符编码格式的CharSet。

(2)如何使用CharSet

得到一个CharSet实例后,我们需要创建一个编码器和一个解码器,使用下面方法进行创建:

       CharSetDecoder decoder = cs.newDecoder();

       CharSetEncoder encoder = cs.newEncoder();

接着我们把ByteBuffer传递给decoder进行编码,返回一个CharBuffer:

       CharBuffer cb = decoder.decode(inputData);

然后我们可以使用encoder进行解码返回一个ByteBuffer:

       ByteBuffer outputData = encoder.encode(cb);

接下来可以进行写等其它操作。

四.Selector(异步IO)

异步IO是一种没有阻塞的读写数据的方法,通常,在代码进行 read() 调用时,代码会阻塞直至有可供读取的数据。同样,write() 调用将会阻塞直至数据能够写入。

异步 I/O 的一个优势在于,它允许您同时根据大量的输入和输出执行 I/O。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步 I/O,您可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程。

异步 I/O 中的核心对象名为 Selector。Selector 就是您注册对各种 I/O 事件的地方,而且当那些事件发生时,就是这个对象告诉您所发生的事件。

第一步:创建一个Selector

       Selector selector = Selector.open();

第二步:打开一个远程连接

       InetSocketAddress socketAddress =

new InetSocketAddress("www.baidu.com", 80);

       SocketChannel sc = SocketChannel.open(socketAddress);

       sc.configureBlocking(false);

第三步:选择键,注册

       SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);

注册时第一个参数总是当前的这个selector。

注册读事件:SelectionKey key = sc.register(selector, SelectionKey.OP_READ);

注册写事件:SelectionKey key = sc.register(selector, SelectionKey.OP_WRITE);

第四步:内部循环处理

int num = selector.select();

Set selectedKeys = selector.selectedKeys();

Iterator it = selectedKeys.iterator();

while (it.hasNext()) {

SelectionKey key = (SelectionKey)it.next();

// ... deal with I/O event ...

}

首先,我们调用 Selector 的 select() 方法。这个方法会阻塞,直到至少有一个已注册的事件发生。当一个或者更多的事件发生时, select() 方法将返回所发生的事件的数量。该方法必须首先执行。

接下来,我们调用 Selector 的 selectedKeys() 方法,它返回发生了事件的 SelectionKey 对象的一个 集合 。

我们通过迭代 SelectionKeys 并依次处理每个 SelectionKey 来处理事件。对于每一个 SelectionKey,您必须确定发生的是什么 I/O 事件,以及这个事件影响哪些 I/O 对象。

第五步:监听事件并做出处理

SelectionKey中共定义了四种事件,OP_ACCEPT(socket accept)、OP_CONNECT(socket connect)、OP_READ(read)、OP_WRITE(write)。

第六步:删除处理过的SelectionKey

在处理 SelectionKey 之后,我们几乎可以返回主循环了。但是我们必须首先将处理过的 SelectionKey 从选定的键集合中删除。

如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活的键出现,这会导致我们尝试再次处理它。

我们调用迭代器的 remove() 方法来删除处理过的 SelectionKey:it.remove();

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页