【问题标题】:Socket Bind Error套接字绑定错误
【发布时间】:2009-07-06 06:30:32
【问题描述】:

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

我能做些什么来解决这个问题?

【问题讨论】:

    标签: java sockets


    【解决方案1】:

    我觉得你可能走得太快了。

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

    当一个套接字关闭时,它会进入特殊的时间等待状态一段时间。这通常是数据包生存时间值的两倍,它可以确保网络中没有数据包正在发送到您的套接字。

    一旦该时间到期,您可以确定网络中的所有数据包都已死亡。套接字处于这种特殊状态,因此当您关闭它时,网络中的数据包可以被捕获并在它们死前到达时被丢弃。

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

    我们在打开大量短期会话的代码时遇到了类似的问题。它运行了一段时间,但随后硬件变得更快,允许在给定的时间段内打开更多。这表现为无法打开更多会话。

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

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

    【讨论】:

    • 您必须根据操作系统(您要连接的机器)配置套接字 time_wait 和其他相关参数
    • 摆弄这些参数并不总是一个好主意。大多数应该针对网络特性进行调整,然后您的应用程序应该针对此进行调整。减少 time_wait 而不减少 time-to-live 将导致虚假数据包到达。将 TTL 减少到数据包无法到达目的地的程度意味着大量丢弃的数据包。理想情况下,您应该保持连接打开(手动或使用连接池)或调整您的应用行为(例如 @Stu 在他的回答中提到的延迟)。
    【解决方案2】:

    我的建议:

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

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

    【讨论】:

    • 其实我有一个睡眠时间,但只有5ms。
    【解决方案3】:

    什么操作系统?如果您使用的是 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 值,以便您有更多动态端口可用于出站连接。接下来,如果这不能为您解决问题,您可以考虑连接的哪一侧应该以TIME_WAIT 结束(假设您可以控制连接上使用的协议......)发出' active close' 是以TIME_WAIT 结尾的那个,所以如果你可以改变一些东西,以便你的服务器发出主动关闭,那么TIME_WAIT 套接字将在服务器上而不是在客户端上积累,这 可能对你更好……

    【讨论】:

    • 确实我正在使用 Windows Xp。我不知道传入连接的限制是什么,但如果它与传出连接相同,那么我只是将问题重新定位到服务器。不过,我认为最大化传出端口是一个好方法。谢谢!
    • 入站连接没有相应的限制。
    • 请注意,这仅适用于通过 XP 的 Windows 客户端,以及通过 2003 的服务器。Vista 和 Server 2008 及更高版本使用新的 TCP/IP 实现,您需要使用netsh interface ipv4 show dynamicportrange tcp 来显示,并且一个相应的set 命令来更改这些值。我手头的 Windows 7 Enterprise 计算机默认有 16384 个动态端口可用,很大程度上消除了这个限制。
    【解决方案4】:

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

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

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

    【讨论】:

    • SO_REUSEADDR 套接字选项仅适用于服务器是用词不当。服务器和客户端都可以使用此选项。
    【解决方案5】:

    我认为这与这个问题相同(我已经链接到我的答案,我认为这可能会有所帮助。)

    Java Bind Exception

    【讨论】:

      【解决方案6】:

      如果示例代码实际上是您执行循环的方式,那么您可能有错误的顺序。

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

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

      【讨论】:

        【解决方案7】:

        使用 socket.close() 后的某个时间不会立即关闭套接字并且循环执行(在循环中它会尝试使用相同 ip 和端口的套接字连接)更快,因此请将套接字设为空。

        socket_server.close();

        socket_server = null;

        谢谢 苏尼尔·库马尔·萨胡

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-31
          • 1970-01-01
          • 2011-08-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-14
          相关资源
          最近更新 更多