如何避免NoRouteToHostException?

披露:我正在做的代码是大学课程作业。

背景:我试图完成的任务是报告不同线程技术的效果。 为此,我写了几个类,它们使用Java套接字响应来自客户端的请求。 这个想法是淹没服务器的请求,并报告不同的线程策略如何处理这个问题。 每个客户端将发出100个请求,并且在每次迭代中,我们将客户端的数量增加50个,直到发生中断。

问题:可重复,持续地发生exception:

引起:java.net.NoRouteToHostException:不能分配请求的地址
    在java.net.PlainSocketImpl.socketConnect(本地方法)
    在java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)

在几种情况下会发生这种情况,包括当客户端和服务器都在本地主机上运行时。 连接可以成功一段时间,试图连接150个引发exception的客户端之后不久。

我首先想到的是,它可能是Linux对开放文件描述符(1024)的限制,但我不这么认为。 我还检查了套接字之间的任何和所有的连接是否正确closures(即在一个正确的finally块中)。

我不愿意发布代码,因为我不确定哪些部分是最相关的,也不希望在问题中有大量代码。

有没有人遇到过这个? 我怎样才能避免NoRouteToHostException?


编辑(进一步的问题是斜体)

到目前为止,一些很好的答案指向了Ephemeral Port Range或RFC 2780.两者都表明我有太多的连接打开。 对于这两个似乎需要达到这个限制的连接数表明,在某些时候我没有closures连接。

debugging了客户端和服务器之后,都观察到这个方法调用myJava-Net-SocketInstance.close() 。 这表明连接正在closures(至less在非例外情​​况下)。 这是一个正确的build议吗?

另外, 是否还需要一个OS级别的等待端口再次变得可用? 如果在运行下一次尝试之前只需要很短的时间(或乐观地运行一个命令),那么对于每个50+客户端来说,运行该程序将是一个单独的时间。


编辑v2.0

在提供了很好的答案后,我修改了我的代码,使用方法setReuseAddress(true)与客户端上build立的每个Socket连接。 这并没有达到预期的效果,我仍然限制在250-300个客户。 程序终止后,运行命令netstat -a显示在TIME_WAIT状态下有很多套接字连接。

我的假设是,如果套接字处于TIME-WAIT状态,并且已经使用SO-REUSEADDR选项设置,那么任何试图使用该端口的新套接字都将能够 – 但是,我仍然收到NoRouteToHostExceptionexception。

它是否正确? 还有什么可以解决这个问题呢?

你试过设置:

 echo "1" >/proc/sys/net/ipv4/tcp_tw_reuse 

和/或

 echo "1" >/proc/sys/net/ipv4/tcp_tw_recycle 

这些设置可能会使Linux重新使用TIME_WAIT套接字。 不幸的是我找不到任何明确的文件。

这可能有助于:

临时端口范围

短暂端口范围的另一个重要分支是它限制了从一台机器到远程机器上特定服务的最大连接数量! TCP / IP协议使用连接的四元组来区分连接,所以如果临时端口范围只有4000个端口,这意味着一次只能有4000个从客户机到远程服务的唯一连接。

所以,也许你用尽了可用的端口。 要获取可用端口的数量,请参阅

 $ cat /proc/sys/net/ipv4/ip_local_port_range 32768 61000 

输出是从我的Ubuntu系统,我有28,232端口的客户端连接。 因此,只要您有280多个客户,您的testing就会失败。

无法分配请求的地址是EADDRNOTAVAIL错误的错误string。

我怀疑你正在用尽源端口。 dynamic范围内有16383个sockets可用作源端口(请参阅RFC 2780 )。 150个客户* 100个连接= 15,000个端口 – 所以你可能达到这个限制。

如果您的源端口用尽,但实际上并没有保持许多打开的连接,请设置SO_REUSEADDR套接字选项。 这将使您能够重用仍处于TIME_WAIT状态的本地端口。

如果你每秒closures500个连接,你将会用尽套接字。 如果您连接到使用Keepalive的相同位置(Web服务器),则可以实现连接池,因此您不必closures并重新打开套接字。

这也将节省CPU。

使用tcp_tw_recycle和tcp_tw_reuse可能会导致从前一个连接进入的数据包,这就是为什么等待数据包清除1分钟的原因。

对于遇到这个问题的任何其他Java用户,我build议使用连接池,以便正确地重用连接。