Search code examples
javaselectselectorsocketchannel

Using Selector on SocketChannel


I'm trying to wait on multiple socket channels for data to read. I've copied example code from the web but it's not working for me. The problem is that the "select" call properly blocks until the first data is received, but thereafter immediately returns 0 (not ready). My code (for a single channel) follows.

    final String HOST = "<REDACTED>.com";
    final int PORT    = 2000;
    SocketChannel MyChannel;
    Selector MySelector;

    void doSelect()
    {
        SelectionKey key;
        int nready;
        long nr;

        try {
            ByteBuffer bytebuf = ByteBuffer.allocate( 1024 );              // allocate bytebuffer
            InetSocketAddress addr = new InetSocketAddress( HOST, PORT);          // form addr
            MyChannel = SocketChannel.open();                   // create socketchan
            MyChannel.connect( addr );                           // connect to data src
            while( !MyChannel.finishConnect() ) Thread.sleep( 1000 ); // wait til connected
            MyChannel.configureBlocking( false );               // set non-blocking
            MySelector = Selector.open();                       // open new selector
            key = MyChannel.register( MySelector, SelectionKey.OP_READ ); // register for reads

            while( true ) {                                     // loop forever
                nready = MySelector.select();                   // block til something ready
                if( nready <= 0 ) {                             // nothing ready?
                    logit( "not ready: select returned "+nready );
                    MyChannel.close();
                    break;                                      // give up
                }
                if( key.isReadable() ) {                        // readable?
                    nr = MyChannel.read( bytebuf );             // read data
                    logit( "read "+nr+" bytes" );
                }
            }   // end while
        }   // end try
        catch( Exception e ) {
            logit( "doSelect: "+e );
            return;
        }
    }

    //@ Log a message to logcat
    void logit(String msg) { Log.i( "MYAPP", msg ); }

UPDATE 10/19/2019

Based on a suggestion by @user207421, I modified the code to iterate over selected key. This made no difference. The select() call is still not blocking after the first reception and the read from the channel says 0 bytes read..

Excerpt from the new code (some variables renamed for (my) readability)

    while( true ) {                                     // loop forever
        nready = selector.select();                   // call select: block til something ready
        if( nready <= 0 ) {                             // nothing ready? (should never happen)
            break;                                      // give up
        }

        // PROCESS ALL SELECTED KEYS
        Set<SelectionKey> selected  = selector.selectedKeys();  // get selected key set
        Iterator<SelectionKey> iter = selected.iterator();    // create iterator
        while( iter.hasNext() ) {                             // while has more
            key = iter.next();                                // get next key
            if( key.isReadable() ) {                           // check if ready to read
                nr = chan.read( bb );                          // read from channel
                if( nr > 0 ) {                                 // got some data?
                  printByteBuffer( bb );
                }
                else {
                  // should never happen! (but it does)
                }
            }
            else if( key.isWritable()) {}                       // never happens!
            else if( key.isAcceptable()) {}                     // never happens!
            else if( key.isConnectable()) {}                    // never happens!
            iter.remove();
        }  // end while hasnext
    }   // end while  

   //@ Convert ByteBuffer to byte array and print
    void printByteBuffer(
      ByteBuffer bb )                       // BB to print
    {
        int nrem;
        byte[] bytes;
        String str;

        bb.flip();                         // reset position
        nrem = bb.remaining();
        bytes = new byte[nrem];            // alloc byte array
        bb.get( bytes );                   // get bytes from bytebuf
        str = new String( bytes );         // cnvt to string
        log( "received \""+str+"\"" );
    }  

Any suggestions greatly appreciated.


Solution

  • I found the solution based on a comment. Apparently the ByteBuffer was "full". I fixed the problem by issuing a ".clear()" on it just before the channel read().

    I don't understand why this was necessary because the ByteBuffer was way bigger (1000 bytes) than any received data, but nontheless this works.

    Re: blocking mode: Every example I found on the web said the channel should be set to non-blocking for Selector select() to work properly. I briefly tried blocking mode anyway but no joy with that.