java Nio 总结学习

来源:互联网 发布:如何用王家卫 知乎 编辑:程序博客网 时间:2024/05/16 14:00
Java Nio
三个主要接口或类
buffer
chanel
selector

一:关于buffer
ByteBuffer.allocate(size)方法确定一个buff的大小 CharBuffer
buffer.put()
向buff中写入数据 写入类型决定于是什么类型的buff  

一般步骤:

1,将数据写入到 Buffer 中.

2,调用 Buffer.flip()方法, 将 NIO Buffer 转换为读模式.

3,从 Buffer 中读取数据

4,调用 Buffer.clear() 或 Buffer.compact()方法, 将 Buffer 转换为写模式.
   rewind() 主要针对于读模式. 在读模式时, 读取到 limit 后, 可以调用 rewind() 方法, 将读 position 置为0.

Buffer.rewind()方法可以重置 position 的值为0, 因此我们可以重新读取/写入 Buffer 了.

多次读取某个数据 可配合使用mark()方法和reset()方法

buffer 有三个属性 position limit和capacity分别表示当前读或写的指针所在位置

调用flip方法时,将limit属性设置为了当前的posion 限制只能读取limit个单位长度的数据

clear方法position属性设置为0,limit设置为capacity。rewind方法只是将position设置为0;

调用flip方法,开始读buff中数据。调用clear,开始往buff中写数据

二:关于chanel
chanel只能与buffer交互
chanel.read(buff) 从buff中读数据到管道中
将buff中数据循环写入管道中
while(buffer.hasRemaining()){
chanel.write(buff)//向管道中写入buff中的数据
}

chanel 管道接受数据流 从网络tcp:SocketChannel,UDP:datagramChannel,文件:FileChannel等.
SocketChannel 是一个客户端用来进行 TCP 连接的 Channel.
SocketChannel socketChannel = SocketChannel.open();// 产生一个SocketChannel
socketChannel.connect(new InetSocketAddress("http://example.com", 80));
ServerSocketChannel tcp服务端 监听某端口的socketChanel的链接
socketChannel.configureBlocking(false);// 设置channel为异步
Selector selector = Selector.open();// 创建选择器
channel.register(selector,SelectorKey.OP_READ)
注册Channel,第一个参数选择器,第二个参数指定对什么类型时间感兴趣,包含Connec,
Accept,Read,Write.
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; // 或运算,关注两种事情


三:关于Selector 选择器,选择哪个chanel处理数据
Selector selector = Selector.open();// 创建选择器
chanel注册到selector上

可以通过 Selector.select()方法获取对某件事件准备好了的 Channel, 即如果我们在注册 Channel 时, 对其的可写事件感兴趣, 那么当 select()返回时, 我们就可以获取 Channel 了.
注意, select()方法返回的值表示有多少个 Channel 可操作.如果 select()方法返回值表示有多个 Channel 准备好了, 那么我们可以通过 Selected key set 访问这个 Channel:
循环调用select()方法查看是否有channel就绪,
selector.selectedKeys() 返回可操作的事件集合
根据selectionKey 调用isAcceptable() 或isConnectable() isReadsble(),isWriteable(),等方法处理不同事件,然后移除处理完的事件keysIterator.remove();
注意, 在每次迭代时, 我们都调用 "keyIterator.remove()" 将这个 key 从迭代器中删除, 因为 select() 方法仅仅是简单地将就绪的 IO 操作放到 selectedKeys 集合中, 因此如果我们从 selectedKeys 获取到一个 key, 但是没有将它删除,
那么下一次 select 时, 这个 key 所对应的 IO 事件还在 selectedKeys 中.

Selector 的基本使用流程

   1 通过 Selector.open() 打开一个 Selector.
   2 将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set)
   3 不断重复:
3.1        调用 select() 方法
3.2        调用 selector.selectedKeys() 获取 selected keys
3.3
        迭代每个 selected key:

3.3.1    *从 selected key 中获取 对应的 Channel 和附加信息(如果有的话)
3.3.2    *判断是哪些 IO 事件已经就绪了, 然后处理它们. 如果是 OP_ACCEPT 事件, 则调用 "SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept()" 获取 SocketChannel, 并将它设置为 非阻塞的, 然后将这个 Channel 注册到 Selector 中.
3.3.4    *根据需要更改 selected key 的监听事件.

3.3.5     *将已经处理过的 key 从 selected keys 集合中删除.


完整范例:

public class NioEchoServer {    private static final int BUF_SIZE = 256;    private static final int TIMEOUT = 3000;    public static void main(String args[]) throws Exception {        // 打开服务端 Socket        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        // 打开 Selector        Selector selector = Selector.open();        // 服务端 Socket 监听8080端口, 并配置为非阻塞模式        serverSocketChannel.socket().bind(new InetSocketAddress(8080));        serverSocketChannel.configureBlocking(false);        // 将 channel 注册到 selector 中.        // 通常我们都是先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ        // 注册到 Selector 中.        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        while (true) {            // 通过调用 select 方法, 阻塞地等待 channel I/O 可操作            if (selector.select(TIMEOUT) == 0) {                System.out.print(".");                continue;            }            // 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪.            Iterator keyIterator = selector.selectedKeys().iterator();            while (keyIterator.hasNext()) {                SelectionKey key = keyIterator.next();                // 当获取一个 SelectionKey 后, 就要将它删除, 表示我们已经对这个 IO 事件进行了处理.                keyIterator.remove();                if (key.isAcceptable()) {                    // 当 OP_ACCEPT 事件到来时, 我们就有从 ServerSocketChannel 中获取一个 SocketChannel,                    // 代表客户端的连接                    // 注意, 在 OP_ACCEPT 事件中, 从 key.channel() 返回的 Channel 是 ServerSocketChannel.                    // 而在 OP_WRITE 和 OP_READ 中, 从 key.channel() 返回的是 SocketChannel.                    SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();                    clientChannel.configureBlocking(false);                    //在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ 注册到 Selector 中.                    // 注意, 这里我们如果没有设置 OP_READ 的话, 即 interest set 仍然是 OP_CONNECT 的话, 那么 select 方法会一直直接返回.                    clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));                }                if (key.isReadable()) {                    SocketChannel clientChannel = (SocketChannel) key.channel();                    ByteBuffer buf = (ByteBuffer) key.attachment();                    long bytesRead = clientChannel.read(buf);                    if (bytesRead == -1) {                        clientChannel.close();                    } else if (bytesRead > 0) {                        key.interestOps(OP_READ | SelectionKey.OP_WRITE);                        System.out.println("Get data length: " + bytesRead);                    }                }                if (key.isValid() && key.isWritable()) {                    ByteBuffer buf = (ByteBuffer) key.attachment();                    buf.flip();                    SocketChannel clientChannel = (SocketChannel) key.channel();                    clientChannel.write(buf);                    if (!buf.hasRemaining()) {                        key.interestOps(OP_READ);                    }                    buf.compact();                }            }        }    }}












原创粉丝点击