为什么connect()会给EADDRNOTAVAIL?

我在我的应用程序中出现了一个似乎不可重现的失败。 我有一个TCP套接字连接失败,应用程序试图重新连接它。 在connect()尝试重新连接的第二个调用中,我得到了errno == EADDRNOTAVAIL的连接()的手册页所表示的错误结果意味着:“指定的地址不是从本地计算机可用。

看着连接()的调用,第二个参数看起来是错误所指的地址,但据我所知,这个参数是远程主机的TCP套接字地址,所以我对这个人感到困惑页面引用本地机器。 这个地址到远程TCP套接字主机是不是从我的本地机器可用? 如果是这样,为什么呢? 它必须成功调用连接()第一次在连接失败之前,它试图重新连接,并得到这个错误。 connect()的参数两次都是一样的。

这个错误是否是一个短暂的错误,如果我尝试再次调用connect可能会消失,如果我等了很久? 如果不是,我应该如何尝试从这个故障中恢复?

检查这个链接

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

编辑 :是的,我的意思是增加更多,但不得不在那里切断,因为紧急情况

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

这里是额外的项目,看看:

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

链接有一个类似于你的错误(答案接近底部)

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

看来你的套接字基本上停留在TCP内部状态之一,并且增加延迟重新连接可能会解决你的问题,因为他们似乎已经在这个错误报告中完成了。

这也可能发生,如果一个无效的端口,如0。

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

首先是打开SO_LINGER零秒超时,迫使TCP堆栈发送一个RST数据包并刷新连接状态。 然而有一个微妙之处:在shutdown之前,你应该在套接字文件描述符上调用shutdown ,以便你有机会在RST包之前发送一个FIN包。 所以代码看起来像这样:

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

如果FIN数据包被RST数据包重新排序,服务器只应该看到一个提前连接重置。

请参阅TCP选项SO_LINGER(零) – 何时需要更多详细信息。 (实验上,你设置setsockopt位置似乎并不重要。)

第二个是使用SO_REUSEADDR和一个显式bind (即使你是客户端),这将允许Linux在运行时重用临时端口,然后再等待。 请注意,您必须使用INADDR_ANY和端口0 bind ,否则SO_REUSEADDR不受尊重。 你的代码看起来像这样:

 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你仍然可以使TCP连接的内部内核数据结构饱和 netstat -an | grep -e tcp -e udp | wc -l netstat -an | grep -e tcp -e udp | wc -l 。 但是,在这种情况下,您将不会开始重用端口。

另一件要检查的是界面已经启动。 最近我在使用网络命名空间时感到困惑,因为它似乎创建了一个新的网络命名空间,它产生了一个完全独立的回送接口,但是并没有提出(至少在Debian版本中)。 这让我有一段时间逃脱了,因为人们通常不会把环回看成是一直停下来的。