tags:

views:

305

answers:

2

Hi I am trying to implements a simple Java NIO server; which registers the socketChannel with the selector. Hence I wish to listen to client and send some response back. After the socketChannel is registered with the selector, even if client(non NIO) sends some data, Server is not able to read; howerver the key generated is still being iterated.

Detailed View: Server Side:

**First thread**:

public void run() { while (true) {

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(true);
    serverSocketChannel.socket().bind(inetAdressOfServer);
    SocketChannel clientChannel = serverSocketChannel.accept();
    new Listener("").addSocketChannel(clientChannel);

}}

**Second Thread**:

    static Selector selector = Selector.open();
    public boolean addSocketChannel(SocketChannel clientChannel) {

        SelectionKey key = clientSocketChannel.register(selector, selector.OP_READ|SelectionKey.OP_WRITE);              
        key.attach(new ChannelCallback(clientSocketChannel));
        return key.isValid();
    }

    public void run() {

        Set keysSet = selector.keys();
        Iterator i = keysSet.iterator();        
        while (i.hasNext()) {
            SelectionKey key = (SelectionKey) i.next();
        }

        if (key.isReadable()) {
            //read and do something
        }
    }



Client Side:

Socket socket = new Socket(serverIP, serverPort);    
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());    
dos.writeBytes(str + "\n");

NB: When done in single thread, the same program works, but when implemented in the manner mentioned above causes it not to listen to client. Please help me to resolve this issue.

+1  A: 

It's very difficult to see what you have done there, but it looks as if what you have marked as "second thread" is used by both threads (some confusion over implementing Runnable/extending Thread and actual threads?). In particular, I'm guessing the new Listener constructs and starts a thread. You are then calling addSocketChannel in the first thread. Therefore, there is a race condition.

Also it's a poor idea to make selector static.

Tom Hawtin - tackline
Hi Tom, sorry the code wasnt clear. The first thread runs in a different class which has logic to accept the socketchannel and passes it to be registered.Please can you let me know what modifications should be made. Thanks.
Nilesh
It looks like the shortest fix that might possibly work is to move the call to addSocketChannel to the Listener constructor (and call it before starting the thread). However, you really need to get your threads sorted out.
Tom Hawtin - tackline
Hi Tom, could you please give me a pointer to learn and improve of NIO threading. Also could you let me know if there is any alternative to static Selector, couldn't get it working with making volatile. Is there any better approach than this.
Nilesh
I've often seen the Rox tutorial mentioned: http://rox-xmlrpc.sourceforge.net/niotut You can also investigate the (completely free to copy) source code to Naga (a NIO wrapper lib) at naga.googlecode.com - the implementation handles many of the common problems such as partial read and writes, close/close after write, asynchronous disconnects, packet conversion etc.
Nuoji
+1  A: 

Read works reading from another thread, here are the obvious problems with your code.

public void run() {
    Set keysSet = selector.keys();

Here you are taking the key set from the iterator, but there is no code ever doing select() or selectNow() on the selector, so this set will always be empty.

    Iterator i = keysSet.iterator();        
    while (i.hasNext()) {
        SelectionKey key = (SelectionKey) i.next();
    }
    if (key.isReadable()) {
        //read and do something
    }
}

This doesn't even compile, the check for 'read' on the key must be done inside the while-block.

SelectionKey key = clientSocketChannel.register(selector,
                                                SelectionKey.OP_READ | 
                                                SelectionKey.OP_WRITE);

Two problems: The channel should be set in non-blocking mode before this is done and SelectionKey.OP_WRITE should not be set unless you want the key to be returned every time you run a select.

You should only set SelectionKey.OP_WRITE if you are actually planning to do a write.

Finally, using two threads here is very unconventional. The recommended way of doing this is to register the ServerSocketChannel to the Selector with OP_ACCEPT, and run the accept on the ServerSocket on the same thread as read/writes.

Nuoji
Thanks a lot Nuoji, I could get the code running. I agree with you regarding concept of thread is unconventional, but its just my try to see performance optimization. Could you please tell me how to mark the key.isReadable to false after reading the data.
Nilesh
Usually you would remove the key from the set after reading, using iterator.remove(). I seem to remember that otherwise it will show up every time you do a select(). After remove() you are guaranteed that the key will not be part of the selected keys until there is data available for read again.Of course, if you want to TURN OFF read interest on the key (i.e. you don't want continue reading on this connection), you should instead use key.interestOps(int ops) to set the new interests for this key (e.g. 0 for neither read nor write).
Nuoji