1

I want to write a client application which send messages to server and receive its reply. The client sends messages many times (for example one message in very second periodically) regardless of replies. When a reply comes back, the client want to respond as quickly as possible.

Here's the code, which does not work, of the client. I want the runnable instance in startReading() method should respond to the reply from the server but it does not. In this case, _channel.write(buffer) does not return properly.

Please let me know the problem of the following code or some other way to implement the above behavior.

public class MyClient {

    private SocketChannel _channel = null;
    private Selector _selector = null;
    private InetSocketAddress _addr = new InetSocketAddress("127.0.0.1", 5555);

    public MyClient () {
        _selector = SelectorProvider.provider().openSelector();
        _channel = SocketChannel.open();
        _channel.configureBlocking(false);
        startReading();
        _channel.connect(_addr);
    }

    private void startReading () throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        _channel.register(_selector, SelectionKey.OP_READ, buffer);
        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                try {
                    while (0 < _selector.select()) {
                        Iterator<SelectionKey> keyIterator = _selector.selectedKeys().iterator();
                        while (keyIterator.hasNext()) {
                            SelectionKey key = keyIterator.next();
                            keyIterator.remove();
                            if (key.isReadable())
                                read(key);
                        }
                    }
                }
                catch (IOException e) {}
            }
        };
        ExecutorService service = Executors.newFixedThreadPool(1);
        service.execute(runnable);
    }

    private void read(SelectionKey key) throws IOException {
        // do some reading operations
    }

    @Override
    public void run() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // write message to buffer
        buffer.flip();
        try {
            _channel.write(buffer);
        } catch (IOException e) {}
    }

    public static void main (String[] args) {
        MyClient client = new MyClient();
        ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
        ex.scheduleAtFixedRate(client, 1000, 1000, TimeUnit.MILLISECONDS);
    }
}
msrd0
  • 7,816
  • 9
  • 47
  • 82
  • 1
    Why do you mess around with selector for a single channel? After all, what you are effectively doing in your background thread is to re-implement a blocking read. So you could simply configure the channel to be blocking and do an ordinary read in your background thread without that complicated structure. – Holger Sep 16 '14 at 18:09
  • Why using selector for single channel ? Because the above code is a simplified model of my problem. What I really want to do is more complicated. – user4047360 Sep 20 '14 at 08:06

1 Answers1

1

You're ignoring the return code from channel.write(). It isn't obliged to write the whole buffer. In non-blocking mode it isn't obliged to write anything at all.

You must do as follows:

  1. While it's returning a positive value, i.e. it has written something, loop.
  2. If it returns zero and buffer.remaining() is zero, you're done: compact() the buffer and return.
  3. If it returns zero and buffer.remaining() is non-zero, the socket send buffer is full, so you have to (a) compact the buffer (b) register for OP_WRITE instead of OP_READ and (c) return to the select loop. When the channel becomes writable, repeat as from (1) above, and this time if you get success as at (2) go back to registering for OP_READ instead of OP_WRITE. In other words you are waiting for the selector to tell you when there is space in the socket send buffer; trying to use it; and if you succeed in completing the write, you're done again.
user207421
  • 305,947
  • 44
  • 307
  • 483