2009-07-06 80 views
4

我有一个测试应用程序,打开一个套接字,通过此套接字发送一些东西,然后关闭它。这是循环完成5-10.000次。问题是,经过反复3,4000我得到这个类型的错误:套接字绑定错误

java.net.BindException: Address already in use: connect 

我甚至设定的插座中immediattly使用,但错误依然存在

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"); 
} 

我能怎么做解决这个问题 ?

回答

11

我想你可能会太快。

大多数操作系统在任何时候都可以打开的套接字数量有限制,但实际上这比其更糟。

当套接字关闭时,它会处于特定的时间等待状态一段时间。这通常是数据包生存时间值的两倍,并且可以确保网络中没有数据包正在通往套接字的路径中。

一旦该时间到期,您可以确定网络中的所有数据包已经死亡。套接字被置于特殊状态,以便在关闭网络时在网络中传出的数据包可以在它们死亡之前到达时被捕获并丢弃。

我认为这就是你的情况,套接字没有像你想象的那样被快速释放。

我们有一个类似的代码打开了很多短暂的会话问题。它运行良好一段时间,但随后硬件变得更快,允许在给定的时间段内打开更多的硬件。这表现为无法开放更多会议。

检查此的一种方法是从命令行执行netstat -a并查看有多少会话实际处于等待状态。

如果情况确实如此,有几种方法可以处理它。

  • 重新使用您的会话,无论是手动或通过维护连接池。
  • 在每个连接中引入延迟尝试并停止达到饱和点。
  • 直到你达到饱和,然后然后修改你的行为,比如在一个while语句内运行你的连接逻辑,每次在完全放弃之前每次重试最多60次,延迟时间为2秒。这可以让您全速运行,只有在出现问题时才会放慢速度。

这最后的子弹一点值得一些扩展。实际上,我们在上述应用中使用了一种退避策略,如果它在抱怨,那么它将逐渐减少资源提供者的负载,而不是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 

这使得处理能够以全速在绝大多数情况下运行,但回退时的错误开始发生,希望给资源提供者的时间来恢复。延迟的逐渐增加允许更严重的资源限制来恢复,并且最大的尝试可以捕捉到您所称的永久性错误(或需要很长时间才能恢复的错误)。

+0

你必须配置插座TIME_WAIT等相关paramters根据不同的操作系统(机器要连接到) – 2009-07-06 06:37:50

+2

它并不总是一个好主意,用这些参数拨弄。大多数应该针对网络特性进行调整,然后您的应用程序应该进行调整。在不减少生存时间的情况下减少time_wait将导致虚假数据包到达。将TTL减少到数据包无法到达目的地意味着大量丢弃的数据包。理想情况下,您应该保持连接处于打开状态(手动或连接池)或调整应用程序行为(例如在他的回答中提及@Stu提及的延迟)。 – paxdiablo 2009-07-06 06:40:44

2

我的建议:

  • 冲洗写操作之后的插座
  • 在上述方法

@Pax的末尾添加一个微小的睡眠(?〜50ms)内,有一个好点关于之后的插座状态。尝试你的代码,让它失败,然后做一个netstat并分析它(或张贴在这里)

+0

其实我有一个睡眠时间,但它只有5ms。 – 2009-07-06 06:48:31

1

我同意别人,你用尽套接字端点。然而,从你的例子来看,这并不是100%清晰的,因为大概这个例外来自一个connect()或bind()调用,这可能是其他一些高级Java方法的基础。

还应该强调,用尽端点并不是套接字库的某种限制,而是任何TCP/IP实现中非常重要的部分。您需要保留关于旧连接的信息一段时间,以便旧连接的迟到IP包丢失。

setReuseAddress()对应于低级别的SO_REUSEADDR套接字选项,并且只在服务器执行listen()时才适用。

+1

SO_REUSEADDR套接字选项仅适用于服务器,这是个误导。这个选项可以被服务器和客户端使用。 – 2009-07-06 08:29:32

2

什么操作系统?如果您使用的是Windows,并且我猜测您是,那么您可以拥有的客户端连接数有限制(这是由MaxUserPort注册表项配置的,默认情况下,它恰好为4000;请参阅http://technet.microsoft.com/en-us/library/aa995661.aspxhttp://www.tech-archive.net/Archive/Windows/microsoft.public.windows.server.general/2008-09/msg00611.html有关更改它的详细信息)。这一点,加上你从客户端启动套接字的事实,因此在你客户端累积的套接字状态可能是你的问题的原因。

请注意,TIME_WAIT积累问题的解决方案不是摆脱TCP堆栈的参数,使问题消失。 TIME_WAIT存在一个很好的理由,并删除或缩短它可能会导致你的新问题!

因此,假设您在Windows机器上,第一步是调整您的MaxUserPort值,以便您有更多动态端口可用于您的出站连接。接下来,如果这不能解决你的问题,你可以考虑连接的哪一端应该以TIME_WAIT(假设你可以控制在你的连接上使用的协议......)发出'active close '是最后一个以TIME_WAIT结尾的,所以如果你可以改变一些东西以便你的服务器发出主动关闭,那么TIME_WAIT插座将会积累在服务器上,而不是在客户端上,这对你来说会更好一些。 ..

0

如果示例代码实际上是你如何执行循环,你可能有错误的顺序的东西。

java docs for setReuseAddress say:当套接字绑定后启用或禁用SO_REUSEADDR时的行为(请参阅isBound())未定义。

尝试在bind()或connect()之前将调用移动到某处。

0

在使用socket.close()后不会立即关闭套接字并且循环执行(在循环中它会尝试使用相同的ip和端口的套接字连接),所以请将套接字置空。

socket_server.close();

socket_server = null;

感谢 苏尼尔·库马尔Sahoo