怎么理解 Java NIO 里面的 attachment ?

问题:每次都能在 Java NIO 里面看到 attach…之类的代码,那么应该怎么理解 attachment 呢?

Java NIONIO2 里面我们经常看到 attachattachment 相关的 API, 如:

Objcet SelectionKey.attch(Object obj)
Object SelectionKey.attachment()
SelectionKey AbstractSelector.register(AbstractSelectableChannel ch,int ops, Object att);

这里的 Attachment 是一个 Object 对象, 也就是说它可以 attach 任何对象, 为什么要 attach 一个对象呢?

我们可以从 BIO 说起, 当 BIO 的时候, 读字节流或字符流的的线程一直 blocking 在读操作上, 不去干其它事情, 当来一个字节/字符的时候, 它就读一个, 直到返回 -1 代表读完(EOF)

可是当时 NIO 的时候, 读线程直接返回, 是由一个专门的 IO 线程操作 Selector 去读取的, 当有部分数据到达的时候, 它就帮你读取, 读完来的数据之后, 还没读取到 EOF, 那么要继续监听这个 socket, 再次等待它接下来的数据, 那么刚才读取的数据放哪里呢?

之后来的数据怎么拼接到原来的数据之后呢? 那么就需要有一个容器放置还没有读完的半成品, 在下次同一个 socket 来数据的时候, 再次拿出这个容器, 继续放数据, 那么这个容器怎么可以容易的每次都有数据的时候拿到呢? 那就放到 SelectionKey 上面吧, 反正每次都是通过 Selectionkey 知道有数据的, 一旦 SelectionKey 有数据, 就通过它拿到之前装有半成品的容器, 那么可以继续放了.

每次读完通过 attach(Object) 方法附在上面, 下次当有数据来的时候, 通过 attachment 方法拿到之前的容器。

NIO 代码演示:

public class NioDemo {

    public static void main(String[] args) {
        try {
            // 开启服务端通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

            // selector 注册器
            Selector selector = Selector.open();

            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(6666));

            // 设置为非阻塞
            serverSocketChannel.configureBlocking(false);

            // 把 channel 注册到 selector 上面
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            // 循环等待客户端连接
            while (true) {
                // 查询如果没有事件发生就可以起做其他的操作了,无需阻塞在这里
                if (selector.select(1000) == 0) {
                    System.out.println("服务器等待1s,无连接");
                    continue;
                }

                // 返回所有等待就绪的事件集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()) {

                    SelectionKey selectionKey = iterator.next();
                    // 针对不同的 selectionKey 去做响应的操作
                    // 发生了客户端连接事件
                    if (selectionKey.isAcceptable()) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        System.out.println("有一个客户端连接成功,hashCode = "+socketChannel.hashCode());
                        // register() 方法 API: public final SelectionKey register(Selector sel, int ops, Object attachment)
                        // 一个 SelectionKey 绑定了一个容器用于存放 Socket 发送过来的数据
                        // 可以通过 attachment() 方法取出这个上一次发送过数据的 Socket,然后把他的数据继续写入到这个绑定的容器
                        socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    }
                    // 发生了服务器读取事件
                    if (selectionKey.isReadable()) {
                        // 通过 selectionKey 获取到这个 Socket 对一个的 Channel
                        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                        // 通过 selectionKey 获取到绑定的容器 ByteBuffer
                        ByteBuffer buffer = (ByteBuffer)selectionKey.attachment();
                        socketChannel.read(buffer);
                        System.out.println("form 客户端 "+ new String(buffer.array()));
                    }
                }
                iterator.remove();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
    }
}

你可能感兴趣的:(Netty,nio)