【问题标题】:Problems with non-blocking select with java.niojava.nio 的非阻塞选择问题
【发布时间】:2010-11-20 11:12:25
【问题描述】:

我刚开始使用 java.nio 并且可能使用了一些错误的方式,所以我遇到了一点问题。

我正在尝试编写一个类似于端口转发器的东西,它可以使用各种附加模块修改通过它的流量。

这是我的做法:

  1. ConnectionManager - 一个线程 有它自己的选择器 注册到 OP_ACCEPT 服务器套接字通道。每当它 选择任何东西 - 它创建一个 ConnectionProcessor 对象 管理连接。

  2. ConnectionProcessor - 一个线程 打开 SocketChannel 到 预定义的前进点(在哪里 从新发送数据包 连接的客户端)。然后它打开 它是自己的选择器并注册它 到客户端的 SocketChannel 的 OP_READ 和服务器的 SocketChannel 的 OP_READ。

然后处理器进入无限循环,从选择器中选择数据并适当地转发。为了确定将数据发送到哪里,它将 SelectionKey.channel() 与 clientChannel 和 serverChannel 进行比较。

ConnectionProcessor 中的选择是在 5 秒的超时 (select(5000)) 下进行的 - 以处理超时。当 select 超时时 - 它会尝试从两个通道中读取以获取异常或 -1 结果。

现在这是我的问题/问题:

  1. 使用key.cancel()对不对 处理密钥后?最多 我在网上看到的例子 只需从 selectedKeys() 列表。键.取消() 似乎是更好的方法。
  2. 有几个对吗 基本上使用 同样的ServerSocketChannel?或者我应该总是使用单个选择器并传递选定的键 合适的经理?我什么 意思是如果 3 个客户端连接 同时,这就是 会发生:

    a) 经理创建处理器。 处理器打开客户端通道。 处理器注册它自己的 选择器到客户端通道。 b) (a) c) 重复 (一)

  3. 出于某种原因,即使一个客户端连接到我的转发器后 - 它也不会处理 消息快于 5000 毫秒超时。它开始选择,锁定 5 秒钟,然后走 到第二次迭代并获取我在上次超时期间收到的 5-6 条消息。 我应该责怪 (1)、(2) 还是其他原因?
  4. 是否有任何手册说明所有 nio 的内部工作原理?我是那种只有在完全理解了底层机制之后才能理解如何使用事物的人。阅读 API 并没有帮助,因为它是为已经知道正确使用 nio 的人编写的。

感谢您阅读我的整个问题,并提前感谢您的帮助。

【问题讨论】:

  • NIO 很难纠正。我会推荐 XNIOMINA 或 insert-NIO-wrapper-library-here 以节省一些麻烦并获得更一致的基础。 “简单”的 NIO 问题之一是需要在数据准备好写入时唤醒选择(这与可写入的通道不同)。编码愉快。
  • 嗯...所以如果我将某些内容放入通道并尝试同时从中进行选择,它在选择完成之前不会发送任何数据?太吓人了,去看看 XNIO 和 MINA。感谢您的建议。
  • 不,他没有这么说。他指的是处理写入时出现的各种问题,并且有多种处理方式。最简单的技术是在选择线程中进行所有内联写入,但即使这样也有其怪癖。

标签: java multithreading sockets nio


【解决方案1】:
  1. 没有。只需从选定的集合中删除密钥。通常这是通过 iterator.remove() 完成的。如果您取消他们的键,它将永远不会被再次选中。

  2. 这毫无意义。您也不需要第二个选择器或额外的线程。这就是蔚来的目的。您可以使用原始线程中的原始选择器来处理这一切。

  3. 这可能是由奇怪的代码引起的。像上面那样重做,看看它是否仍然发生。如果是这样,请在此处发布一些代码。

  4. 您需要阅读 Berkeley Sockets API 或一本好书,例如 Stevens、Unix Network Programming 或我的 ;-)

【讨论】:

  • 感谢您的回答,马上尝试。至于 (4) - 我不是指 windows Sockets,我指的是 nio 部分。比如选择器的工作原理等等。
  • 另外,我不确定数字 (2)。对数据包的额外修改可能需要很长时间(如 2-5 秒),而我想同时保持多个客户端连接。那我应该怎么做呢?
  • NIO 只是在底层使用 select() 来进行初步近似。你所说的(2)不会改变任何事情。您只需选择直到发生某些事情,然后处理它:OP_READ 或 OP_ACCEPT。
  • @Max 所以你让(单个)选择器本身阻塞——它可以监控 n 个通道,维护读/写数据(在可读数据上,但不监控写- 能够直到数据准备好...),根据需要唤醒选择器,等等。这......很乏味。
  • @EJP 似乎 (1) 是问题所在。一旦我将其更改为删除迭代器,它就奇迹般地开始工作了。但是,您能否解释一下 cancel() 到底是做什么的?我为什么要关心这个键永远不会再次被选中(我已经在处理它了)?还是取消选择器中的所有键?谢谢。
猜你喜欢
  • 2014-03-08
  • 1970-01-01
  • 2015-05-15
  • 2015-05-05
  • 1970-01-01
  • 2013-12-28
  • 2015-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多