Selector

java.nio.channels.Selector Example

This example shows the basic usage of Selector. This is an abstract class defined in the java.nio.channels package.

Selector is a multiplexor of SelectableChannel objects.

NOTE: From the thefreedictionary.com a data-multiplexer is defined as a multiplexer that permits two or more data sources to share a common transmission medium.

Multiplexed, non-blocking I/O, which is much more scalable than thread-oriented, blocking I/O, is provided by the classes Selector, SelectableChannel, and SelectionKey. A SelectableChannel can be multiplexed via a selector. DatagramChannel, Pipe.SinkChannel, Pipe.SourceChannel, ServerSocketChannel and SocketChannel classes extend SelectableChannel.

Description

  • A selector is a multiplexor of selectable channels, which in turn are a special type of channel that can be put into non-blocking mode.
  • To perform multiplexed I/O operations, one or more selectable channels are first created, put into non-blocking mode, and registered with a selector.
  • Registering a channel specifies the set of I/O operations that will be tested for readiness by the selector, and returns a selection key that represents the registration.
  • Once some channels have been registered with a selector, a selection operation can be performed in order to discover which channels, if any, have become ready to perform one or more of the operations in which interest was previously declared.
  • If a channel is ready then the key returned when it was registered will be added to the selector’s selected-key set.
  • The key set, and the keys within it, can be examined in order to determine the operations for which each channel is ready. From each key one can retrieve the corresponding channel in order to perform whatever I/O operations are required.

1. The Example

This example uses ServerSocketChannel and SocketChannel classes to define server and client. The example has two programs; a main program which defines the server socket channel and the selector, and a client socket channel program.

The main program code is explained here:

1.1. Create a selector

Selector selector = Selector.open();

The selector is created by invoking the open() static method of the selector class.

1.2. Open a server socket channel

ServerSocketChannel serverSocket = ServerSocketChannel.open();
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 5454);
serverSocket.bind(hostAddress);

1.3. Register the channel with the selector

First, set this channel to non-blocking mode.

serverSocket.configureBlocking(false);

Next, get the server socket channel’s supported operations.

int ops = serverSocket.validOps();

The server socket channel’s validOps() method returns an operation set identifying this channel’s supported operations i.e., accepting new connections. The SelectionKey class has variables defining the operation sets. The OP_ACCEPT (socket-accept operation) is the only valid value for server socket channel.

Register this channel with the given selector, returning a SelectionKey. Selection key is a token representing the registration of a selectable channel with a selector.

SelectionKey selectKy = serverSocket.register(selector, ops, null); // null for an attachment object

1.4. The selection process

The selector’s select() method selects a set of keys whose corresponding channels are ready for I/O operations. This method performs a blocking 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 selectedKeys() method returns this selector’s selected-key set. The key set, and the keys within it, can be examined in order to determine the operations for which each channel is ready. From each key one can retrieve the corresponding channel in order to perform whatever I/O operations are required.

int noOfKeys = selector.select();

Set selectedKeys = selector.selectedKeys();
Iterator iter = selectedKeys.iterator();

while (iter.hasNext()) {

    SelectionKey ky = iter.next();

    if (ky.isAcceptable()) {

        // Accept the new client connection
        SocketChannel client = serverSocket.accept();
        client.configureBlocking(false);

        // Add the new connection to the selector
        client.register(selector, SelectionKey.OP_READ);
    }
    else if (ky.isReadable()) {
 
        // Read the data from client
        SocketChannel client = (SocketChannel) ky.channel();
        ...

2. The Code

2.1. The main program

SelectorExample.java

import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Set;
import java.util.Iterator;
import java.net.InetSocketAddress;

public class SelectorExample {

    public static void main (String [] args)
            throws IOException {

        // Get selector
        Selector selector = Selector.open();

        System.out.println("Selector open: " + selector.isOpen());

        // Get server socket channel and register with selector
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 5454);
        serverSocket.bind(hostAddress);
        serverSocket.configureBlocking(false);
        int ops = serverSocket.validOps();
        SelectionKey selectKy = serverSocket.register(selector, ops, null);

        for (;;) {

            System.out.println("Waiting for select...");
            int noOfKeys = selector.select();

            System.out.println("Number of selected keys: " + noOfKeys);

            Set selectedKeys = selector.selectedKeys();
            Iterator iter = selectedKeys.iterator();

            while (iter.hasNext()) {

                SelectionKey ky = iter.next();

                if (ky.isAcceptable()) {

                    // Accept the new client connection
                    SocketChannel client = serverSocket.accept();
                    client.configureBlocking(false);

                    // Add the new connection to the selector
                    client.register(selector, SelectionKey.OP_READ);

                    System.out.println("Accepted new connection from client: " + client);
                }
                else if (ky.isReadable()) {

                    // Read the data from client

                    SocketChannel client = (SocketChannel) ky.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    client.read(buffer);
                    String output = new String(buffer.array()).trim();

                    System.out.println("Message read from client: " + output);
	
                    if (output.equals("Bye.")) {

                        client.close();
                        System.out.println("Client messages are complete; close.");
                    }

                } // end if (ky...)

                iter.remove();

            } // end while loop

        } // end for loop
    }
}

2.1. The client program

SocketClientExample.java

import java.nio.channels.SocketChannel;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.net.InetSocketAddress;

public class SocketClientExample {

    public static void main (String [] args)
            throws IOException, InterruptedException {

        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 5454);
        SocketChannel client = SocketChannel.open(hostAddress);

        System.out.println("Client sending messages to server...");

        // Send messages to server
		
        String [] messages = new String [] {"Time goes fast.", "What now?", "Bye."};

        for (int i = 0; i < messages.length; i++) {

            byte [] message = new String(messages [i]).getBytes();
            ByteBuffer buffer = ByteBuffer.wrap(message);
            client.write(buffer);

            System.out.println(messages [i]);
            buffer.clear();
            Thread.sleep(3000);
        }

        client.close();				
    }
}

3. The Output

The main program is started first and then the client program (use two terminals).

3.1. The main program output

Selector open: true
Waiting for select...
Number of selected keys: 1
Accepted new connection from client: java.nio.channels.SocketChannel[connected local=/127.0.0.1:5454 remote=/127.0.0.1:50686]
Waiting for select...
Number of selected keys: 1
Message read from client: Time goes fast.
Waiting for select...
Number of selected keys: 1
Message read from client: What now?
Waiting for select...
Number of selected keys: 1
Message read from client: Bye.
Client messages are complete; close.
Waiting for select...

From the output:

  • Accepted new connection from client: java.nio.channels.SocketChannel[connected local=/127.0.0.1:…]: This message is displayed after the client program is started. This shows the client connection is accepted by the server.
  • Message read from client: Time goes fast. This shows the earlier accepted client’s first message is read.

3.2. The client program output

Client sending messages to server...
Time goes fast.
What now?
Bye.

4. Download Java Source Code

This was an example of java.nio.channels.Selector

Download
You can download the full source code of this example here: SelectorExamples.zip

Prasad Saya

Prasad Saya is a software engineer with over ten years’ experience in application development, maintenance, testing and consulting on various platforms. He is a certified Java and Java EE developer. At present his interest is in developing Java applications. He also has experience working with databases and ERP applications.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button