Java NIO在从一个套接字读取并写入另一个套接字时存在固有缺陷吗?

3

我正在研究一个基于NIO和socket通道的最小服务器概念,可以将数据从一个端口流向另一个目标。

当网络速度很快时,一切都运行良好。但是当一侧比另一侧更快时,情况非常糟糕,会导致CPU占用率大幅增加,效率极低。

例如:

Iterator keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
    SelectionKey key = (SelectionKey) keys.next();
    keys.remove();
    try {
        if (!key.isValid()) continue;
        if (key.isReadable()) read(key);
    }
    catch (Exception e) {
        e.printStackTrace();
        key.cancel();
    }
}

当套接字有可读数据时,就会发生读取调用。但是如果由于客户端延迟高或只是不能快速读取数据而无法将此套接字的可写部分写入,会怎样呢?我们最终会以疯狂的速度循环执行无操作,直到更多可写缓冲区释放出来:

public void read(SelectionKey key) throws IOException {
    ByteBuffer b = (ByteBuffer) buffers.get(readable); //prior buffer that may or may not have been fully used from the prior write attempt
    int bytes_read = readable.read(b);
    b.flip();
    if (bytes_read > 0 || b.position() > 0) writeable.write(b);
    b.compact();
}

假设我们可以以每秒1千兆比特的速度从套接字中读取数据,但是接收方只能以每秒100千比特的速度从我们可写的套接字中读取...因此,由于客户端并不能像我们希望的那样快速消耗套接字缓冲区中的数据,我们可能需要在每个小数据块之间循环一百万次,才能将其写入客户端。
如果使用线程进行操作,则没有问题,因为我们会在写入调用上阻塞。但是使用NIO时,你应该怎么做才能让它跳过读取通知呢?
1个回答

1
我明白了。虽然我没有找到指向它的文档,但经过更多考虑后,我意识到这就是OP_WRITEABLE场景的用途。
因此,当可写返回接受了0字节时,我会在可读通道上注销OP_READ,并在可写通道上注册OP_WRITEABLE。一旦我得到可写通知,我就会交换回读/写通道,并包括它们所注册的内容,现在删除OP_WRITEABLE。
因此,当写入无法完成时,请注销触发您尝试向其写入的读取,并改为在其上注册写入通知。一旦您获得通知,就可以交换回注册。
现在一切运行良好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接