如何避免NoRouteToHostException?

时间:2009-10-15 12:58:32

标签: java networking sockets exception-handling

披露:我正在处理的代码是大学课程。

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

问题:可重复且一致地发生异常:

Caused by: java.net.NoRouteToHostException: Cannot assign requested address
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)

在多种情况下都会发生这种情况,包括客户端和服务器都在localhost上运行。连接可以成功一段时间,在尝试连接150个客户端之后很快就会抛出异常。

我的第一个想法是它可能是Linux对开放文件描述符(1024)的限制,但我不这么认为。我还检查了套接字之间的所有连接是否正确关闭(即在正确的finally块内)。

我对发布代码犹豫不决,因为我不确定哪些部分最相关,并且不希望在问题中有大量代码。

有没有人遇到过这个?如何避免NoRouteToHostException?


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

到目前为止,一些好的答案指向短暂的端口范围或RFC 2780.这两个都表明我有太多的连接打开。对于两者而言,似乎需要达到此限制的连接数表明在某些时候我没有关闭连接。

调试了客户端和服务器后,两者都被观察到方法调用myJava-Net-SocketInstance.close()。这表明连接正在关闭(至少在非例外情​​况下)。 这是正确的建议吗?

此外,是否需要等待操作系统等级以使端口再次可用?如果只需要短路,则可以为每个50多个客户端单独运行该程序在运行下一次尝试之前(或乐观地,运行一个命令)。


EDIT v2.0

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

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

这是对的吗? 还有什么方法可以解决这个问题吗?

6 个答案:

答案 0 :(得分:51)

您是否尝试过设置:

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

和/或

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

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

答案 1 :(得分:16)

这可能有所帮助:

The Ephemeral Port Range

  

另一个重要的衍生物   短暂的端口范围是它的限制   来自的最大连接数   一台机器到一个特定的服务   远程机器! TCP / IP协议   使用连接的4元组   区分连​​接,所以如果   短暂的港口范围只有4000   港口宽,这意味着可以   只有4000个独特的连接来自a   客户端机器到远程服务处   一次。

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

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

输出来自我的Ubuntu系统,我有28,232个端口用于客户端连接。因此,只要有280多个客户,您的测试就会失败。

答案 2 :(得分:9)

无法分配请求的地址是EADDRNOTAVAIL错误的错误字符串。

我怀疑你的源端口已经用完了。动态范围内有16,383个套接字可用作源端口(请参阅RFC 2780)。 150个客户端* 100个连接= 15,000个端口 - 因此您可能达到此限制。

答案 3 :(得分:5)

如果您的源端口不足但实际上没有维护那么多打开的连接,请设置SO_REUSEADDR套接字选项。这将使您能够重用仍处于TIME_WAIT状态的本地端口。

答案 4 :(得分:1)

如果您每秒关闭500个连接,则将耗尽套接字。如果要连接到使用keepalive的相同位置(Web服务器),则可以实现连接池,因此不要关闭并重新打开套接字。

这也会节省cpu。

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

答案 5 :(得分:0)

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