套接字绑定错误
我有一个测试应用程序打开一个套接字,通过这个套接字发送一些东西,然后关闭它。 这循环完成5-10.000次。 问题是在3,4000次迭代后我得到了这种类型的错误:
java.net.BindException: Address already in use: connect
我甚至将套接字设置为立即使用,但错误仍然存在
try { out_server.write(m.ToByteArray()); socket_server.setReuseAddress(true); socket_server.close(); } catch(Exception e) { e.printStackTrace(); System.out.println(i+" unable to register with the server"); }
我该怎么做才能解决这个问题?
我想你可能会走得太快。
大多数操作系统都限制了它们在任何时候都可以打开的插槽数量,但它实际上比这更糟糕。
当套接字关闭时,它将处于特定的时间等待状态一段时间。 这通常是数据包生存时间值的两倍,它可以确保网络中没有仍有数据包出现在您的套接字上。
一旦该时间到期,您可以确保网络中的所有数据包都已经死亡。 套接字处于特殊状态,以便在关闭时将网络中的数据包丢弃,如果它们在死亡之前到达,则可以将其丢弃。
我认为这就是你的情况,sockets并没有像你想象的那样快速释放。
我们遇到了类似的问题,代码打开了很多短暂的会话。 它运行良好一段时间,但随后硬件变得更快,允许在给定的时间段内打开更多。 这表明自己无法开设更多会议。
检查这一点的一种方法是从命令行执行netstat -a
,看看有多少会话实际处于等待状态。
如果情况确实如此,那么有几种方法可以处理它。
- 手动或通过维护连接池重用会话。
- 在每个连接中引入延迟以尝试并停止到达饱和点。
- 直到你达到饱和状态然后修改你的行为,例如在while语句中运行你的连接逻辑,每次重试最多60次,每次延迟两秒,然后完全放弃。 这使您可以全速运行,只有在出现问题时才会减速。
最后一个要点值得一些扩展。 我们实际上在前面提到的应用程序中使用了退避策略,如果它抱怨的话,它会逐渐减轻资源提供者的负担,而不是30秒延迟,我们选择了一秒钟的延迟,然后是两秒钟,然后是四个等等。
退避策略的一般过程如下,它可以在任何可能暂时缺乏资源的情况下使用。 下面的伪代码中提到的操作将是在您的情况下打开套接字。
set maxdelay to 16 # maximum time period between attempts set maxtries to 10 # maximum attempts set delay to 0 set tries to 0 while more actions needed: if delay is not 0: sleep delay attempt action if action failed: add 1 to tries if tries is greater than maxtries: exit with permanent error if delay is 0: set delay to 1 else: double delay if delay is greater than maxdelay: set delay to maxdelay else: set delay to 0 set tries to 0
这允许进程在绝大多数情况下以全速运行,但在错误开始时退出,希望为资源提供者提供恢复时间。 延迟的逐渐增加允许更严格的资源限制来恢复,并且最大尝试捕获您所谓的永久性错误(或者需要花费太长时间才能恢复的错误)。
我的建议:
- 写入后刷新套接字
- 在上述方法结束时添加一个小睡眠(~50ms?)
之后@Pax对套接字的状态有一个很好的观点。 尝试你的代码,让它失败,然后做一个netstat
并分析它(或发布在这里)
什么操作系统? 如果您正在使用Windows,我猜你是,那么您可以拥有的客户端连接数量有限(这是由MaxUserPort
注册表项配置的,默认情况下恰好是4000;请参阅http ://technet.microsoft.com/en-us/library/aa995661.aspx和http://www.tech-archive.net/Archive/Windows/microsoft.public.windows.server.general/2008-09/msg00611 .html有关更改它的详细信息)。 这与您从客户端关闭套接字并因此在客户端上累积TIME_WAIT
状态的套接字这一事实可能是导致问题的原因。
请注意, TIME_WAIT
累积问题的解决方案不是摆弄TCP堆栈的参数以使问题消失。 TIME_WAIT
存在的原因非常充分,删除或缩短它可能会导致新问题!
因此,假设您使用的是Windows计算机,第一步是调整MaxUserPort
值,以便为出站连接提供更多动态端口。 接下来,如果这不能解决问题,您可以考虑连接的哪一端应该以TIME_WAIT
结束(假设您可以控制连接上使用的协议……)发出’主动关闭的对等方’是以TIME_WAIT
结束的那个,所以如果你可以更改东西以便你的服务器发出主动关闭,那么TIME_WAIT
套接字将在服务器而不是客户端上积累,这对你来说可能更好……
我同意其他人说你的套接字端点用完了。 但是,从您的示例中可能并非100%清晰,因为可能是exception来自connect()或bind()调用,该调用可能是其他一些高级Java方法的基础。
还应该强调的是,耗尽端点不是套接字库的某种限制,而是任何TCP / IP实现的基本部分。 您需要保留有关旧连接的信息一段时间,以便丢弃旧连接的迟到IP数据包。
setReuseAddress()对应于低级SO_REUSEADDR套接字选项,仅在执行listen()时应用于服务器。
我认为这与这个问题是一样的(我和我的答案有关,我认为这可能会有所帮助。)
Java绑定exception
如果示例代码实际上是您执行循环的方式,那么您可能会以错误的顺序执行操作。
setReuseAddress的java文档说:绑定套接字后启用或禁用SO_REUSEADDR时的行为(请参阅isBound())未定义。
尝试在bind()或connect()之前将调用移动到某个位置。
使用socket.close()之后的某个时候不会立即关闭套接字并且循环执行(在循环中它尝试与同一个ip和端口的套接字连接)要快得多,所以请将套接字置零。
socket_server.close();
socket_server = null;
谢谢Sunil Kumar Sahoo