【NIO】解读 java.nio.channels.Selector

喜欢而已 提交于 2020-08-04 11:10:15

目录

Part 1. What's Selector?

Part 2. Why Selector?

Part 3. How to use Selector?

1) Create Selector

2) Channel's Register

3) Selection Key

4) Selector.select()

5) Selector.selectedKeys()

Part 4. Demo Train


Part 1. What's Selector?

Selector is the core conception for NIO from JDK1.4 which is a multiplexor of SelectableChannel objects that allows one thread to handle multiple channels.

 

Part 2. Why Selector?

Before NIO came out, We used Socket programming like this:

//---Server---

//Create ServerSocket and bind the port
ServerSocket serverSocket = ……;
serverSocket.bind(8899);

While (true) {
    // Blocking to monitor new connection 
    Socket socket = serverSocket.accept(); 
    
    // Create a new tread for each socket connection, 
    new Thread(socket);

    // New thread to deal with socket raed & write.
    run() {
        ……
        socket.read();
        ……
        socket.write();
    }
}


//---Client---
Socket socket = new Socket(localhost, ‘8899’);
socket.connect();

Alanysis :

1) As you see each socket connection will create new thread. Thread switching is expensive for CPU.

2) this model is relatively good, In the case that the number of active connections is not particularly high (less than 1000 for a single machine),  allowing each connection to focus on its own I/O and the programming model is simple.

 

Part 3. How to use Selector?

1) Create Selector

Selector selector = Selector.open();

A selector may be created by invoking the open method of this class, which will use the system's default  selector provider to create a new selector.  A selector may also be created by invoking the openSelector method of a custom selector provider.  A selector remains open until it is closed via its close method.

 

2) Channel's Register

Code like this:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

Registers this channel with the given selector, returning a selection key.

public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;

1)If this channel is currently registered with the given selector then the selection key representing that registration is returned.  The key's interest set will have been changed to ops, as if by invoking the interestOps(int) method.  If the att argument is not null then the key's attachment will have been set to that value.

2)Otherwise this channel has not yet been registered with the given selector, so it is registered and the resulting new key is returned. The key's initial interest set will be ops and its attachment will be att.

3)This method may be invoked at any time. If this method is invoked while another invocation of this method or of the configureBlocking method is in progress then it will first block until the other operation is complete.  This method will then synchronize on the selector's key set and therefore may block if invoked concurrently with another registration or selection operation involving the same selector.

 

3) Selection Key

A token representing the registration of a SelectableChannel with a Selector.

A selection key is created each time a channel is registered with a selector. A key remains valid until it is 'cancelled' by invoking its 'cancel' method, by closing its channel, or by closing its selector.  Cancelling a key does not immediately remove it from its selector; it is instead added to the selector's 'cancelled-key set' for removal during the next selection operation.  The validity of a key may be tested by invoking its 'isValid' method.

 

'Interest Set' && 'Ready Set'

A selection key contains two 'operation sets' represented as integer values.  Each bit of an operation set denotes a category of selectable operations that are supported by the key's channel.

1) 'interest set'

This determines which operation categories will be tested for readiness the next time one of the selector's selection methods is invoked.  The interest set is initialized with the value given when the key is created; it may later be changed via the 'interestOps(int)' method.

 

2) 'ready set'

This identifies the operation categories for which the key's channel has been detected to be ready by the key's selector, which indicates that its channel is ready for some operation category is a hint, but not a guarantee, that an operation in such a category may be performed by a thread without causing the thread to block.

 

3) 'Operation Set'

* OP_READ

If the selector detects that the corresponding channel is ready for reading, has reached end-of-stream, has been remotely shut down for further reading, or has an error pending, then it will add 'OP_READ' to the key's ready-operation set and add the key to its 'selected-key' set.

- boolean isReadable()

Tests whether this key's channel is ready for reading.

 

* OP_WRITE

If the selector detects that the corresponding channel is ready for writing, has been remotely shut down for further writing, or has an error pending, then it will add 'OP_WRITE' to the key's ready set and add the key to its selected-key set.

- boolean isWritable()

Tests whether this key's channel is ready for writing.

 

* OP_CONNECT

If the selector detects that the corresponding socket channel is ready to complete its connection sequence, or has an error pending, then it will add 'OP_CONNECT' to the key's ready set and add the key to its 'selected-key' set.

- boolean isConnectable();

Tests whether this key's channel has either finished, or failed to finish, its socket-connection operation.

 

* OP_ACCEPT

If the selector detects that the corresponding server-socket channel is ready to accept another connection, or has an error pending, then it will add  OP_ACCEPT to the key's ready set and add the key to its selected-key set.

- boolean isAcceptable()

Tests whether this key's channel is ready to accept a new socket connection.

 

4) Selector.select()

int num = selector.select();

Selects a set of keys whose corresponding channels are ready for I/O operations.

This method performs a blocking 'selection operation'.  It returns only after at least one channel is selected, this selector's 'wakeup' method is invoked, or the current thread is interrupted, whichever comes first.

The returned num means number of keys, possibly zero, whose ready-operation sets were updated.

 

5) Selector.selectedKeys()

Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

The 'selected-key set' is the set of keys such that each key's channel was detected to be ready for at least one of the operations identified in the key's interest set during a prior selection operation. This set is returned by the selectedKeys() method.

We can use this method to get channels which is operatable. Code shows like this:

Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
    SelectionKey key = iterator.next();
    if (key.isConnectable()) {
        System.out.println("connected");
    }
    else if (key.isAcceptable()) {
        System.out.println("accepted");
    }
    else if (key.isReadable()) {
        System.out.println("readed");
    }
    iterator.remove();
}

Note: By invoking iterator to operate select method, We need to remove the iterator manually for select() just collect the ready set of channel, for next time select(), it still exist.

 

Part 4. Demo Train

public static void main(String[] args) throws Exception{
    //
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.socket().bind(new InetSocketAddress(8080));
    serverSocketChannel.configureBlocking(false);
    // 开启selector
    Selector selector = Selector.open();
    // serverSckChannel 注册
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);


    while (true) {
        // select 开始轮询,检测已就绪的Event
        int num = selector.select();
        if (num > 0) {
            // 遍历 已就绪的Event
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                final SocketChannel client;
                SelectionKey key = iterator.next();
                if (key.isConnectable()) {
                    System.out.println("===connected===");
                }
                // 监测 新的tcp连接
                else if (key.isAcceptable()) {
                    System.out.println("===accepted===");
                    ServerSocketChannel server = (ServerSocketChannel)key.channel();
                    // 注册 read Event
                    client = server.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                }
                // 监测 可读的连接
                else if (key.isReadable()) {
                    System.out.println("===readed===");
                    client = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    client.read(buffer);
                    buffer.flip();
                    // 打印 内容
                    Charset charset = Charset.forName("utf-8");
                    String receiveMsg = String.valueOf(charset.decode(buffer).array());
                    System.out.println(client + ":" + receiveMsg);
                }
                iterator.remove();
            }
        }
    }
}

Remember that 'Event' is the core conception of Selector.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!