java nio 选择器 returns 选定的键没有可用的操作
java nio selector returns selected keys with no operations available on them
我正在尝试使用 java nio 类 在 java 中编写套接字服务器。只使用非阻塞io,不使用async io。
我有一个调用 selector 的 select() 方法的线程。最初 select 列表中的唯一通道是 ServerSocketChannel。每次 select() returns 我枚举 selectedKeys() 列表并将 io 委托给单独的 io 线程。在我将信息发送到 io 线程之前,我首先尝试通过将 interestOps 设置为零来关闭频道上的兴趣。我这样做是为了 select() 不再在该频道上触发,因为它应该继续触发直到执行 io 。顺便说一下,您无法通过 SelectionKey 的 cancel() 方法完成此操作,因为该方法会将频道置于已取消的列表中,并且无法将其添加回 selection 列表。 cancel() 仅用于在完成后从列表中删除频道。我根本不使用 cancel(),因为关闭 socket() 会自动调用 cancel()。
当 io 线程完成 io 时,它会与 select 线程通信,要求它关闭套接字或将 interestOps 重置为它们应有的状态。
虽然这似乎有些工作,但我注意到 select() 似乎持续 return 套接字通道,即使没有字节可读。顺便说一下,服务器只从套接字读取并且只将兴趣设置为 OP_READ(或者当我想关闭通道时为零)。为了测试一个特定的场景,我编写了一个打开套接字的客户端,向其写入 1k 字节,休眠 10 秒,然后向其写入另外 1k 字节,然后关闭套接字。在此休眠期间,select() 连续调用 returns,当我读取套接字时,returned 为零字节。显然,我不希望 select() 循环在没有工作可做时连续触发,因为我会固定 CPU。我已经为非阻塞 io 设置了通道。我还应该提到的是,与我在下面引用的线程之一类似,这种行为在客户端打开套接字并开始发送数据之前不会发生。在这种情况下,我仍然有正在监听 OP_ACCEPT 的服务器套接字通道,并且 select() 调用不会为此不断触发。
这里有两个相似但不同的线程:
Infinite loop with Selector even there's no connection from client
Java Selector returns SelectionKey with OP_READ without data in infinity loop after writing to channel
这两个线程中的一些评论讨论了处理 EOF,-1 on read() 的情况。这不是我遇到的问题,所以请不要将此标记为重复。
此外,在其中一个线程中,听起来有人建议,由于套接字通道处于非阻塞模式,因此 select() 将连续触发,无论是否有数据,在我的例子中读。这听起来很奇怪。我很难相信这是它应该工作的方式。我相信当我表示我有兴趣在一个频道上阅读时,select() 应该只 return selected 列表中的那个频道,如果有数据要读取的话。
谢谢,
尼克
听起来您在迭代时并未从所选键集中删除所选键。在您链接的线程中有几个正确执行此操作的示例。
NB 其中一个声明如果没有事件,select()
将 无限期地循环 。只有当超时值为正时才会发生这种情况:它将超时并且 return 重复为零。如果没有超时,它将无限期阻塞。
注意 cancel()
实际上不会阻止您将频道添加回选择列表。您将不得不重新注册频道,如果 select()
在取消和重新注册之间没有执行(因为它可能没有处理其内部取消列表),则可能会出现问题。但是你现在的做法没有任何问题。
我正在尝试使用 java nio 类 在 java 中编写套接字服务器。只使用非阻塞io,不使用async io。
我有一个调用 selector 的 select() 方法的线程。最初 select 列表中的唯一通道是 ServerSocketChannel。每次 select() returns 我枚举 selectedKeys() 列表并将 io 委托给单独的 io 线程。在我将信息发送到 io 线程之前,我首先尝试通过将 interestOps 设置为零来关闭频道上的兴趣。我这样做是为了 select() 不再在该频道上触发,因为它应该继续触发直到执行 io 。顺便说一下,您无法通过 SelectionKey 的 cancel() 方法完成此操作,因为该方法会将频道置于已取消的列表中,并且无法将其添加回 selection 列表。 cancel() 仅用于在完成后从列表中删除频道。我根本不使用 cancel(),因为关闭 socket() 会自动调用 cancel()。
当 io 线程完成 io 时,它会与 select 线程通信,要求它关闭套接字或将 interestOps 重置为它们应有的状态。
虽然这似乎有些工作,但我注意到 select() 似乎持续 return 套接字通道,即使没有字节可读。顺便说一下,服务器只从套接字读取并且只将兴趣设置为 OP_READ(或者当我想关闭通道时为零)。为了测试一个特定的场景,我编写了一个打开套接字的客户端,向其写入 1k 字节,休眠 10 秒,然后向其写入另外 1k 字节,然后关闭套接字。在此休眠期间,select() 连续调用 returns,当我读取套接字时,returned 为零字节。显然,我不希望 select() 循环在没有工作可做时连续触发,因为我会固定 CPU。我已经为非阻塞 io 设置了通道。我还应该提到的是,与我在下面引用的线程之一类似,这种行为在客户端打开套接字并开始发送数据之前不会发生。在这种情况下,我仍然有正在监听 OP_ACCEPT 的服务器套接字通道,并且 select() 调用不会为此不断触发。
这里有两个相似但不同的线程:
Infinite loop with Selector even there's no connection from client
Java Selector returns SelectionKey with OP_READ without data in infinity loop after writing to channel
这两个线程中的一些评论讨论了处理 EOF,-1 on read() 的情况。这不是我遇到的问题,所以请不要将此标记为重复。
此外,在其中一个线程中,听起来有人建议,由于套接字通道处于非阻塞模式,因此 select() 将连续触发,无论是否有数据,在我的例子中读。这听起来很奇怪。我很难相信这是它应该工作的方式。我相信当我表示我有兴趣在一个频道上阅读时,select() 应该只 return selected 列表中的那个频道,如果有数据要读取的话。
谢谢, 尼克
听起来您在迭代时并未从所选键集中删除所选键。在您链接的线程中有几个正确执行此操作的示例。
NB 其中一个声明如果没有事件,select()
将 无限期地循环 。只有当超时值为正时才会发生这种情况:它将超时并且 return 重复为零。如果没有超时,它将无限期阻塞。
注意 cancel()
实际上不会阻止您将频道添加回选择列表。您将不得不重新注册频道,如果 select()
在取消和重新注册之间没有执行(因为它可能没有处理其内部取消列表),则可能会出现问题。但是你现在的做法没有任何问题。