Question

I've built a Selector-based system in Java that can accept multiple clients. It has a ServerSocketChannel registered under OP_ACCEPT, which accept()s the incoming connection and registers the resulting SocketChannel with the selector again. Here's that bit:

ServerSocketChannel insock = ServerSocketChannel.open();
    insock.configureBlocking(false);
    insock.socket().bind(new InetSocketAddress(6789));

    Selector sel = Selector.open();
    SelectionKey joinchannel = insock.register(sel, SelectionKey.OP_ACCEPT);

    System.out.println("Ready to accept incoming connections.");

    while (true) {
        int ready = sel.selectNow();
        if (ready == 0)
            continue;
        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey key = keyIterator.next();
            if(key.isAcceptable()){
                SocketChannel newConnection = insock.accept();
                System.out.println("New client "+newConnection+" connected.");
                newConnection.configureBlocking(false);
                newConnection.register(sel, SelectionKey.OP_READ).attach(new DGPlayer());
            }

If I register the new SocketChannel for OP_READ, this works fine. The check for isReadable() succeeds, reads data. Here's that bit:

else if(key.isReadable()){
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(trans > 0){
                    DGPlayer client = (DGPlayer) key.attachment();
                    System.out.println(client.name+": "+msg.trim());
                }
            }
                //              else if(key.isWritable()){
//                  ByteBuffer buf = ByteBuffer.allocate(48);
//                  buf.clear();
//                  buf.put(((String)key.attachment()).getBytes());
//                  buf.flip();
//                  
//                  SocketChannel target = ((SocketChannel)key.channel());
//                  while(buf.hasRemaining()) {
//                      target.write(buf);
//                  }
//                  
//                  key.attach(null);
//              }

If I register the SocketChannel for OP_READ|OP_WRITE, however, nothing happens. As far as I can tell, the selector never finds the channel to be either readable() or writable(). The only change is registering the channel for WRITE permission.

Any idea why this might be?

EDIT - For completion's sake, here's some client code:

while (true) {
        int readyChannels = sel.select();
        if (readyChannels == 0)
            continue;

        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

        while (keyIterator.hasNext()) {
            SelectionKey key = keyIterator.next();

            if (key.isReadable()) {
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(msg.trim().length() != 0)
                    System.out.println("Response from server: "+msg.trim());
            }
            else if(key.isWritable() && messages.size() > 0){
                String message = messages.remove();
                ByteBuffer buf = ByteBuffer.allocate(48);
                buf.clear();
                buf.put(message.getBytes(Charset.forName("UTF-8")));

                buf.flip();

                int written = outsock.write(buf);
                while(buf.hasRemaining()){
                    outsock.write(buf);
                }

                System.out.println("Wrote '"+message+"' to server");
            }
            keyIterator.remove();
        }
    }

(The Client registers the Server for OP_READ|OP_WRITE)

Was it helpful?

Solution

In general it's a bad idea to have a channel registered for OP_WRITE if you don't have anything to actively write to it. Most channels can be written to most of the time, so every call to Selector.select will return without blocking and consume resources. It's better to add the writing flag to SelectionKey.interestOps when you're ready to write something to the channel, and remove the flag when you're done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top