套接字绑定错误

时间:2009-07-06 06:30:33

标签: java sockets

我有一个打开套接字的测试应用程序,通过此套接字发送内容然后关闭它。这循环完成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");
}

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

7 个答案:

答案 0 :(得分:11)

我想你可能会走得太快。

大多数操作系统对任何时候都可以打开的插槽数量有限制,但实际上比这更糟糕。

当套接字关闭时,它将处于特定的时间等待状态一段时间。这通常是数据包生存时间值的两倍,它可以确保网络中没有仍然有数据包出现在您的套接字上。

一旦该时间到期,您可以确定网络中的所有数据包都已经死亡。套接字处于特殊状态,以便在关闭时将网络中的数据包丢弃,如果它们在死亡之前到达,则可以将其丢弃。

我认为这就是你的情况,插座并没有像你想象的那样快速释放。

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

检查这一点的一种方法是从命令行执行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

这允许进程在绝大多数情况下以全速运行,但在错误开始时退出,希望给资源提供者时间恢复。延迟的逐渐增加允许更严格的资源限制来恢复,并且最大尝试会捕获您所谓的永久性错误(或者需要花费很长时间才能恢复的错误)。

答案 1 :(得分:2)

我的建议:

  • 在写入
  • 后刷新套接字
  • 在上述方法的最后添加一个小睡眠(~50ms?)

@Pax之后对套接字的状态有一个很好的观点。尝试使用您的代码,让它失败,然后执行netstat并进行分析(或在此处发布)

答案 2 :(得分: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状态的套接字这一事实可能是导致问题的原因。

请注意,TIME_WAIT累积问题的解决方案不是摆弄TCP堆栈的参数以使问题消失。 TIME_WAIT存在的原因非常充分,删除或缩短它可能会给您带来新的问题!

因此,假设您使用的是Windows计算机,第一步是调整MaxUserPort值,以便为出站连接提供更多动态端口。接下来,如果这不能解决问题,您可以考虑连接的哪一端应该以{{1​​}}结束(假设您可以控制连接上使用的协议......)发布的对等方'主动关闭'是以TIME_WAIT结尾的那个,所以如果你可以改变一些事情以便服务器发出主动关闭,那么TIME_WAIT套接字将在服务器而不是客户端上累积这个 MAY 对你更好......

答案 3 :(得分:1)

我同意其他人说你的套接字端点用完了。但是,从您的示例中可能并非100%清晰,因为可能是异常来自connect()或bind()调用,该调用可能是其他一些高级Java方法的基础。

还应该强调的是,耗尽端点不是套接字库的某种限制,而是任何TCP / IP实现的基本部分。您需要保留有关旧连接的信息一段时间,以便丢弃旧连接的迟到IP数据包。

setReuseAddress()对应于低级SO_REUSEADDR套接字选项,仅在执行listen()时应用于服务器。

答案 4 :(得分:0)

我认为这与这个问题是一样的(我的答案与我的答案相关,我认为这可能有所帮助。)

Java Bind Exception

答案 5 :(得分:0)

如果示例代码实际上是您执行循环的方式,则可能会出现错误的顺序。

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

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

答案 6 :(得分:0)

使用socket.close()之后的某个时候不会立即关闭套接字并且循环执行(在循环中它尝试套接字连接同样的ip和端口)要快得多,所以请将套接字置零。

socket_server.close();

socket_server = null;

由于 Sunil Kumar Sahoo