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