如何避免 NoRouteToHostException?
披露:我正在编写的代码用于大学课程。
背景:我试图完成的任务是报告不同线程技术的效果。为此,我编写了几个类,它们使用 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)
这种情况会在多种情况下发生,包括当客户端和服务器都在本地主机上运行时。暂时可以成功连接,尝试连接150个客户端后不久就抛出异常。
我的第一个想法是这可能是 Linux 对打开文件描述符的限制(1024),但我不这么认为。我还检查了套接字之间的所有连接是否已正确关闭(即在正确的finally
块内)。
我对发布代码犹豫不决,因为我不确定哪些部分最相关,并且不想在问题中列出大量代码。
以前有人遇到过这个吗?如何避免 NoRouteToHostException?
编辑(进一步的问题用斜体字表示)
到目前为止,一些好的答案指向临时端口范围或 RFC 2780。这两者都表明我打开了太多连接。对于两者来说,达到此限制所需的连接数表明在某些时候我不会关闭连接。
调试完客户端和服务器后,我们发现两者都调用了 myJava-Net-SocketInstance.close()
方法。这表明连接正在关闭(至少在非例外情况下)。 这是一个正确的建议吗?
另外,是否需要操作系统级别的等待才能使端口再次可用? 可以为每个端口单独运行该程序50+ 客户端,如果在运行下一次尝试之前只需要很短的时间(或者乐观地,运行命令)。
编辑 v2.0
接受了所提供的良好答案后,我修改了代码,以对客户端上建立的每个 Socket 连接使用 setReuseAddress(true) 方法。这并没有达到预期的效果,我的客户数量仍然限制在 250-300 名。程序终止后,运行命令netstat -a
可以看到有大量的socket连接处于TIME_WAIT状态。
我的假设是,如果套接字处于 TIME-WAIT 状态,并且已使用 SO-REUSEADDR 选项进行设置,则尝试使用该端口的任何新套接字都将被能够 - 但是,我仍然收到 NoRouteToHostException。
这是正确的吗? 还有什么办法可以解决这个问题吗?
Disclosure: the code I'm working on is for university coursework.
Background: The task I'm trying to complete is to report on the effect of different threading techniques. To do this I have written several classes which respond to a request from a client using Java Sockets. The idea is to flood the server with requests and report on how different threading strategies cope with this. Each client will make 100 requests, and in each iteration we're increasing the number of clients by 50 until something breaks.
Problem: repeatably, and consistently, an exception occurs:
Caused by: java.net.NoRouteToHostException: Cannot assign requested address at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
This happens in several scenarios, including when both the client and server are running on localhost. Connections can be made successfully for a while, it's soon after trying to connect 150 clients that the exception is thrown.
My first thought was that it could be Linux's limit on open file descriptors (1024) but I don't think so. I also checked that any and all connections between the sockets are closed properly (i.e. within a correct finally
block).
I'm hesitant to post the code because I'm not sure which parts would be the most relevant, and don't want to have a huge listing of code in the question.
Has anyone come across this before? How can I avoid the NoRouteToHostException?
EDIT (further questions are italicised)
Some good answers so far which point to either the The Ephemeral Port Range or RFC 2780. Both of which would suggest that I have too many connections open. For both it appears the number of connections which need to be made to reach this limit suggest that at some point I'm not closing connections.
Having debugged both client and server, both have been observed to hit the method call myJava-Net-SocketInstance.close()
. This would suggest that connections are being closed (at least in the non-exceptional case). Is this a correct suggestion?
Also, is there an OS level wait required for ports to become available again? It would be a possibility to run the program a separate time for each 50+ clients if it would just require a short period (or optimistically, running a command) before running the next attempt.
EDIT v2.0
Having taken the good answers provided, I modified my code to use the method setReuseAddress(true) with every Socket connection made on the client. This did not have the desired effect, and I am still limited to 250-300 clients. After the program terminates, running the command netstat -a
shows that there is a lot of socket connections in the TIME_WAIT status.
My assumption was that if a socket was in the TIME-WAIT
status, and had been set with the SO-REUSEADDR
option, any new sockets attempting to use that port would be able to - however, I am still receiving the NoRouteToHostException.
Is this correct?
Is there anything else which can be done to solve this problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您是否尝试过设置:
和/或
这些设置可能会使 Linux 重新使用 TIME_WAIT 套接字。不幸的是我找不到任何明确的文档。
Have you tried setting:
and/or
These settings may make Linux re-use the TIME_WAIT sockets. Unfortunately I can't find any definitive documentation.
这可能会有所帮助:
临时端口范围
所以也许你用完了可用的端口。要获取可用端口的数量,请参阅
输出来自我的 Ubuntu 系统,其中有 28,232 个端口用于客户端连接。因此,一旦您有超过 280 个客户端,您的测试就会失败。
This may help:
The Ephemeral Port Range
So maybe you run out of available ports. To get the number of available ports, see
The output is from my Ubuntu system, where I'd have 28,232 ports for client connections. Hence, your test would fail as soon as you have 280+ clients.
无法分配请求的地址是 EADDRNOTAVAIL 错误的错误字符串。
我怀疑你的源端口用完了。动态范围内有 16,383 个套接字可用作源端口(请参阅 RFC 2780)。 150 个客户端 * 100 个连接 = 15,000 个端口 - 所以您可能会达到此限制。
Cannot assign requested address is the error string for the EADDRNOTAVAIL error.
I suspect you are running out of source ports. There are 16,383 sockets in the dynamic range available for use as a source port (see RFC 2780). 150 clients * 100 connections = 15,000 ports - so you are probably hitting this limit.
如果您用完了源端口,但实际上并没有维护那么多打开的连接,请设置
SO_REUSEADDR
套接字选项。这将使您能够重用仍处于 TIME_WAIT 状态的本地端口。If you're running out of source ports but aren't actually maintaining that many open connections, set the
SO_REUSEADDR
socket option. This will enable you to reuse local ports that are still in theTIME_WAIT
state.如果每秒关闭 500 个连接,您将耗尽套接字。如果您连接到使用 keepalive 的相同位置(Web 服务器),您可以实现连接池,这样您就不必关闭并重新打开套接字。
这也会节省CPU。
使用 tcp_tw_recycle 和 tcp_tw_reuse 可能会导致数据包从先前的连接传入,这就是为什么要等待 1 分钟才能清除数据包。
If you are closing 500 connection per second you will run out of sockets. If you are connecting to the same locations (web servers) that use keepalive you can implement connection pools, so you don't close and reopen sockets.
This will save cpu too.
Use of tcp_tw_recycle and tcp_tw_reuse can result in packets coming in from the previous connecction, that is why there is a 1 minute wait for the packets to clear.
对于任何其他偶然发现这个问题的 Java 用户,我建议使用连接池,以便正确地重用连接。
For any other Java users that stumble across this question, I would recommend using connection pooling so connections are reused properly.