Languages/Java2013.04.15 11:16


Java NIO and reactor pattern.Java NIO and reactor pattern. http://www.moparscape.org/smf/index.php?topic=460976.0


The key component of java NIO is java.nio.channels.Selector. With this, you can easily monitor a specific set of events happening from underlying channels. With this, you can easily write codes to create a socket, bind the socket, listen the socket, and accept a new client connection. 


Following is an example code from a working system. 


Selector accept_selector = Selector.open();


ServerSocketChannel tcp_server = ServerSocketChannel.open();

tcp_server.socket().bind(new InetSocketAddress(6633));

tcp_server.configureBlocking(false);

tcp_server.register( accept_selector, SelectionKey.OP_ACCEPT );


//

// start accept loop

// 

int accept_seq = 0;

while ( !quit ) {

int r = accept_selector.select();


if ( r > 0 ) {

// accept set is ready

Set<SelectionKey> keys = accept_selector.selectedKeys();

for ( Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {

SelectionKey key = i.next();

i.remove();


if ( key.isAcceptable() ) {

int seq = ++accept_seq;

SocketChannel sw_channel = tcp_server.accept();

sw_channel.configureBlocking(false);

sw_channel.socket().setTcpNoDelay(true);

sw_channel.socket().setPerformancePreferences(0,2,3);


some_thread_object.addClient( sw_channel );

}

}

}

}



Above code is very simple, so I think no further explanation is needed. One thing to note is that you can also apply this kind of convention to the socket read. After creating sw_channel, you might pass the channel to a thread that monitors a set of connections. Then, the thread does 'select' on the set of connections, check if there are some readable connections. On the readable connections, you can actually perform 'read'. 


void addClient(SocketChannel client) {

synchronized ( guard ) {

try {

// ...

client.register( 

read_selector.wakeup(), 

SelectionKey.OP_READ | SelectionKey.OP_WRITE, 

null /* attachment */

);

} catch (ClosedChannelException e) {

// channel is closed. 

try {

client.close();

} catch (IOException e1) {

// does nothing.

}

}

}

}


Above code is to register a ClientChannel object to a 'read_selector' object, that the thread object has as its private member. One thing to note is, how the 'guard' object is used to prevent deadlock at client.register() and read_selector.select() call. This is an idiom, so please follow it. 


while ( !quit ) {

try {

// guard idiom to prevent deadlock at client.register() call

synchronized (guard) {}


int r = read_selector.select();

if ( r > 0 ) { // there's something to read.


Set<SelectionKey> keys = read_selector.selectedKeys();

for ( Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {

SelectionKey key = i.next();

i.remove();

try { 

if ( !key.isValid() ) {

// do something

key.cancel();

conn.close();

continue;

}

if ( key.isWritable() ) {

// do something

}

if (  key.isReadable() && !handleReadEvent(conn) ) {

// do something

key.cancel();

conn.close();

}

} catch ( CancelledKeyException e ) {

e.printStackTrace();

continue;

}

}

}

} catch (IOException e) {

e.printStackTrace();

// just break this watcher.

return;

}

}


Above codes are from the run loop of  'some_thread_object'. Basically, this loop monitors a set of channels that read and write is possible. Once the selector selected a set of keys (each key is mapped to one SocketChannel object) that are readable or writable, you should check teach channel is actually readable using key.isReadable(), or writable using key.isWritable(). If they return true, that means you are able to read from or write to the channel. 


May the NIO be with you!


저작자 표시 비영리 변경 금지
신고
Posted by 이병준

소중한 의견, 감사합니다. ^^