简体   繁体   English

java.nio.channels.SocketChannel用于定期写入和立即读取

[英]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. 我希望startReading()方法中的runnable实例应响应来自服务器的答复,但事实并非如此。 In this case, _channel.write(buffer) does not return properly. 在这种情况下, _channel.write(buffer)无法正确返回。

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() . 您将忽略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. 如果返回零, 并且 buffer.remaining()为零,则说明操作完成: compact()缓冲区并返回。
  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. 如果返回零并且buffer.remaining() 为零,则套接字发送缓冲区已满,因此您必须(a)压缩缓冲区(b) OP_WRITE而不是OP_READ寄存器,并且(c)返回选择循环。 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 . 当通道变为可写状态时,请从上面的(1)开始重复,这一次,如果您在(2)处获得成功,请返回注册OP_READ而不是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. 如果成功完成写入,则可以再次完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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