2017-10-10 63 views
2

对于练习,我们将实现一个服务器,该服务器具有侦听连接的线程,接受它们并将套接字引入BlockingQueue。池中的一组工作线程然后通过队列并处理通过套接字进入的请求。Java:使用队列管理比线程更多的连接

每个客户端连接到服务器,发送大量的请求(在发送下一个请求之前等待响应),并最终在完成时断开连接。

我目前的方法是让每个工作线程在队列中等待,获取套接字,然后处理一个请求,并最终将(仍然打开的)套接字放回队列中,然后再处理另一个请求, 。有更多的客户端比工作线程多,所以很多连接排队。

此方法的问题:即使客户端不发送任何内容,线程也会被客户端阻止。可能的伪解决方案,都不能令人满意:在inputStream

  • 呼叫available(),并把连接回到队列中,如果返回0的问题:这是不可能的检测,如果客户端仍连接。
  • 如上所述,但使用socket.isClosed()socket.isConnected()来确定客户端是否仍连接。问题:这两种方法都没有检测到客户端挂起,正如EJP在Java socket API: How to tell if a connection has been closed?
  • 中很好地描述的一样,通过读取或写入客户端来探测客户端是否仍在那里。问题:读取块(即回到非活动客户端阻塞队列的原始情况),并且写入实际上会将某些内容发送给客户端,从而使测试失败。

有没有办法解决这个问题?即是否有可能区分一个断开的客户端与被动客户端没有阻止或发送的东西?

回答

1

发现问题可以通过一些技巧来解决。有几个人长时间的讨论之后,我结合自己的想法,把工作中reasonnable时间完成:

  • 创建套接字后,将其配置使得阻塞read会禁止对特定时间,比如100毫秒:socket.setSoTimeout(100);
  • 此外,记录每个连接上次成功读取的时间戳,例如与System.currentTimeMillis()
  • 原则上(见下面的例外这个原则),在读取前在连接上运行available()。如果这返回0,则将连接放回队列中,因为没有可读的内容。
  • 上述原则的例外情况,在这种情况下,不使用available():如果时间戳太旧(例如,超过1秒),请使用read()实际阻止连接。这将不会比您在上面为套接字设置的SoTimeout更长。如果您遇到TimeoutException,请将连接重新放回队列中。如果您读取-1,则将该连接从远端关闭后丢弃。

有了这个策略,大多数读取尝试立即终止,要么返回一些数据,要么什么也不是,因为没有任何东西available(),所以它们被跳过。如果另一端关闭了连接,我们将在一秒内检测到这一点,因为上次成功读取的时间戳太旧。在这种情况下,我们执行实际读取操作,返回-1,并且相应地更新套接字的isClosed()。而在套接字仍然打开但队列很长以至于我们有超过一秒的延迟的情况下,我们需要100ms才能发现连接仍然存在,但尚未准备就绪。

编辑:一个增强功能是将“上次成功读取”更改为“上次阻塞读取”,并在得到TimeoutException时更新时间戳。

3

简答:没有。如需更长的回答,请参阅EJP的回答。

这就是为什么你可能不应该把套接字放回队列,而是处理所有来自套接字的请求,然后关闭它。将连接传递给不同的工作线程以单独处理请求不会带来任何好处。

如果您的客户端运行状况不佳,您可以在套接字上使用读取超时,因此只有发生超时时,读取才会阻塞。然后,您可以关闭该套接字,因为您的服务器没有时间迎合那些表现不佳的客户端。

2

有没有办法解决这个问题?即是否有可能区分一个断开的客户端与被动客户端没有阻止或发送的东西?

当使用阻塞IO时不是真的。

你可以看看非阻塞(NIO)包,它处理的事情有点不同。

实质上,你有一个套接字可以注册一个“选择器”。如果您将套接字注册为“可准备读取数据”,则可以确定要读取哪些套接字而无需单独进行轮询。

同样的写作内容。

这里是一个tutorial on writing NIO servers

+0

如果我理解正确,这个问题与阻塞IO无关,可用()中的问题用于解决这个问题。相反,问题在于如何判断客户是否意外关闭,或只是安静。我认为使用NIO对此并无帮助。 –

+0

我试图处理更大的查询'这种方法的问题:即使客户端不发送任何东西,线程也会被客户端阻止。两个模型都存在断开连接的客户端,但至少在NIO中,您并不等待它们。 – ptomli

+0

同样可以使用available()no?还没有等待。 –

0

号,辨别从客户端没有正确关闭其插座不活动的客户端的唯一方法是发送ping或东西来检查,如果他们还在那里。

可能的解决方案,我可以看到的是,没有一会送什么

  • 踢的客户。你将不得不跟踪他们已经安静了多久,一旦他们达到极限,你就认为他们已经断开了。
  • Ping客户端,看他们是否还在那里。我知道你要求一种不发送任何东西的方法,但如果这确实是一个问题,也就是说你不能使用上述解决方案,这可能是根据具体情况做到的最好方法(因为它是一个练习你可能不得不想象具体细节)。
  • 两者的混合,实际上这可能会更好。跟踪他们平静多久,稍微给他们发一个ping,看他们是否还活着。