【问题标题】:java.nio.channels.SocketChannel for both periodic write and immediate readjava.nio.channels.SocketChannel 用于定期写入和立即读取
【发布时间】:2014-09-16 17:56:37
【问题描述】:

我想编写一个向服务器发送消息并接收其回复的客户端应用程序。无论回复如何,客户端都会多次发送消息(例如,每隔一秒定期发送一条消息)。当回复回来时,客户希望尽快做出响应。

这是客户端的代码,它不起作用。我希望startReading() 方法中的可运行实例应该响应来自服务器的回复,但它没有。在这种情况下,_channel.write(buffer) 不会正确返回。

请让我知道以下代码的问题或实现上述行为的其他方式。

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

【问题讨论】:

  • 你为什么要为单个频道使用选择器?毕竟,您在后台线程中有效地做的是重新实现阻塞读取。因此,您可以简单地将通道配置为阻塞并在后台线程中进行普通读取,而无需复杂的结构。
  • 为什么对单通道使用选择器?因为上面的代码是我的问题的简化模型。我真正想做的事情更复杂。

标签: java client selector nio socketchannel


【解决方案1】:

您忽略了来自channel.write() 的返回码。它没有义务写入整个缓冲区。在非阻塞模式下,它根本不需要写任何东西。

您必须执行以下操作:

  1. 当它返回一个正值时,即它写了一些东西,循环。
  2. 如果它返回零并且buffer.remaining() 是零,你就完成了:compact() 缓冲区并返回。
  3. 如果它返回零并且buffer.remaining()-零,则套接字发送缓冲区已满,因此您必须 (a) 压缩缓冲区 (b) 为 OP_WRITE 注册而不是OP_READ 和 (c) 返回到选择循环。当通道变为可写时,从上面的 (1) 开始重复,这次如果你在 (2) 处获得成功,则返回注册 OP_READ 而不是 OP_WRITE。换句话说,您正在等待选择器告诉您套接字发送缓冲区何时有空间;尝试使用它;如果你成功完成了写入,那么你又完成了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    相关资源
    最近更新 更多