java Nio 总结学习

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();
                }
            }
        }
    }
}
   
   












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值