代码之家  ›  专栏  ›  技术社区  ›  WilliamKF

为什么connect()会给eaddrnotavil?

  •  12
  • WilliamKF  · 技术社区  · 15 年前

    我的申请中出现了一个似乎无法重现的失败。我有一个TCP套接字连接失败,应用程序试图重新连接它。在第二次尝试重新连接connect()的调用中,我得到一个errno==eaddrnotavil的错误结果,connect()的手册页上说:“指定的地址在本地计算机上不可用。”

    看一下对connect()的调用,第二个参数似乎是错误引用的地址,但据我所知,这个参数是远程主机的TCP套接字地址,所以我对引用本地计算机的手册页感到困惑。本地计算机上没有远程TCP套接字主机的地址吗?如果是,为什么会这样?它必须在连接失败前第一次成功地调用connect(),并尝试重新连接并得到此错误。connect()的参数两次都是相同的。

    如果我再次尝试呼叫connect,如果我等待足够长的时间,这个错误会不会是暂时的?如果没有,我应该如何从失败中恢复过来?

    4 回复  |  直到 12 年前
        1
  •  20
  •   David    15 年前

    检查此链接

    http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html

    编辑 :是的,我想再加一点,但由于紧急情况,我不得不把它剪掉

    在尝试重新连接之前是否关闭了套接字?关闭将告诉系统socketpair(ip/端口)现在是空闲的。

    以下是一些附加项目,请看:

    • 如果本地端口已经连接到给定的远程IP和端口(即,已经有一个相同的socketpair),您将收到此错误(请参阅下面的错误链接)。
    • 绑定非本地套接字地址将产生此错误。如果机器的IP地址是127.0.0.1和1.2.3.4,并且试图绑定到1.2.3.5,则会出现此错误。
    • eaddrnotavil:指定的地址在远程计算机上不可用,或者名称结构的地址字段全部为零。

    链接到一个类似于你的bug(答案很接近底部)

    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599

    似乎您的套接字基本上处于某个TCP内部状态,添加重新连接的延迟可能会解决您的问题,就像他们在错误报告中所做的那样。

        2
  •  3
  •   mkerley    15 年前

    如果给定的端口无效(如0),也可能发生这种情况。

        3
  •  2
  •   Community Mohan Dere    9 年前

    如果您不愿意更改可用的临时端口数(如David建议的那样),或者您需要的连接数超过理论最大值,则有两种其他方法可以减少正在使用的端口数。但是,它们在不同程度上违反了TCP标准,因此应该谨慎使用。

    首先是打开 SO_LINGER 以零秒超时,强制 TCP 堆栈以发送RST数据包并刷新连接状态。不过,有一个微妙之处:你应该 shutdown 在套接字文件描述符上 close ,以便您有机会发送 FIN 包在 RST 小包裹。所以代码看起来像:

    shutdown(fd, SHUT_RDWR);
    struct linger linger;
    linger.l_onoff = 1;
    linger.l_linger = 0;
    // todo: test for error
    setsockopt(fd, SOL_SOCKET, SO_LINGER,
               (char *) &linger, sizeof(linger));
    close(fd);
    

    只有在 数据包被重新排序为 RST公司 小包裹。

    TCP option SO_LINGER (zero) - when it's required 更多细节。(从实验上看,你的设置似乎并不重要 setsockopt .)

    二是使用 SO_REUSEADDR 以及一个明确的 bind (即使您是客户机),这将允许Linux在运行时重用临时端口,在它们完成等待之前。注意你 必须 使用 绑定 具有 INADDR_ANY 和港口 0 ,否则 所以你要做的就是 不受尊重。您的代码将类似于:

    int opts = 1;
    // todo: test for error
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
             (char *) &opts, sizeof(int));
    
    struct sockaddr_in listen_addr;
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_port = 0;
    listen_addr.sin_addr.s_addr = INADDR_ANY;
    // todo: test for error
    bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr));
    
    // todo: test for addr
    // saddr is the struct sockaddr_in you're connecting to
    connect(fd, (struct sockaddr *) &saddr, sizeof(saddr));
    

    这个选项不太好,因为您仍然会按照 netstat -an | grep -e tcp -e udp | wc -l . 但是,在这种情况发生之前,您不会开始重用端口。

        4
  •  1
  •   Surendra Mobiya    7 年前

    我有这个问题。我通过启用tcp时间戳解决了这个问题。

    根本原因:

    1. 连接关闭后,连接将进入时间等待状态 时间。

    2. 在此状态下,如果任何新连接具有相同的IP和端口, 如果在创建套接字期间未提供sou REUSEADDR,则socket bind() 将失败,错误为EADDRINUSE。

    3. 但是,即使在提供了sou REUSEADDR also socket connect()之后 如果两边都未启用tcp时间戳,则失败并返回错误EADDRNOTAVAIL。

    解决方案: 请在客户端和服务器端启用tcp时间戳。

    echo 1>/proc/sys/net/ipv4/tcp_时间戳

    启用tcp时间戳的原因:

    当我们启用tcp_tw_reuse时,可以在套接字过期之前使用处于TIME_WAIT状态的套接字,内核将尝试确保没有关于tcp序列号的冲突。如果我们启用tcp_时间戳,它将确保这些冲突不会发生。但是,我们需要在两端启用TCP时间戳。有关血淋淋的详细信息,请参见tcp_twsk_unique的定义。

    参考: https://serverfault.com/questions/342741/what-are-the-ramifications-of-setting-tcp-tw-recycle-reuse-to-1

        5
  •  0
  •   ZakW user562374    11 年前

    另一件要检查的事情是接口是否启动。我最近在使用网络名称空间时被这个弄糊涂了,因为它似乎创建了一个新的网络名称空间,生成了一个完全独立的环回接口,但并没有显示出来(至少在Debian wheezy的版本中是这样)。这让我有一段时间没有想到,因为人们通常不会认为环回已经关闭。