简体   繁体   中英

java.nio.channels.SocketChannel for both periodic write and immediate read

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);
    }
}

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, ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM