我的应用程序中出现了一个似乎无法重现的故障。我有一个TCP套接字连接失败,应用程序尝试重新连接它。在第二次调用connect()尝试重新连接时,我得到了errno == EADDRNOTAVAIL的错误结果,connect()的手册页表示:“指定的地址不能从本地计算机上获得。”
看看对connect()的调用,第二个参数似乎是错误引用的地址,但据我所知,这个参数是远程主机的TCP套接字地址,所以我很困惑关于引用本地机器的手册页。难道我的本地计算机无法访问远程TCP套接字主机的这个地址吗?如果是这样,为什么会这样呢?它必须在连接失败之前第一次成功调用connect()并尝试重新连接并获得此错误。 connect()的参数两次都是相同的。
这个错误会不会是一个短暂的错误,如果我再试一次呼叫连接,如果我等了足够长的话,可能会消失吗?如果没有,我应该如何从这次失败中恢复?
答案 0 :(得分:19)
检查此链接
http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html
编辑:是的我想增加更多但是由于紧急情况不得不在那里削减
在尝试重新连接之前,您是否关闭了套接字?关闭将告诉系统socketpair(ip / port)现在是空闲的。
以下是其他项目:
链接与您的类似的错误(答案接近底部)
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599
看起来您的套接字基本上停留在其中一个TCP内部状态,并且为重新连接添加延迟可能会解决您的问题,因为它们似乎已经在该错误报告中完成了。
答案 1 :(得分:3)
如果给出了无效的端口,例如0,也会发生这种情况。
答案 2 :(得分:1)
如果您不愿意更改可用的临时端口数(如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);
如果FIN
数据包与RST
数据包重新排序,服务器应该只看到过早的连接重置。
有关详细信息,请参阅TCP option SO_LINGER (zero) - when it's required。 (实验上,设置setsockopt
的位置似乎并不重要。)
第二种方法是使用SO_REUSEADDR
和显式bind
(即使您是客户端),这将允许Linux在您运行之前重新使用临时端口,然后才能完成等待。请注意,必须使用bind
INADDR_ANY
和端口0
,否则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连接的内部内核数据结构饱和。但是,在这种情况发生之前,您不会开始重复使用端口。
答案 3 :(得分:1)
我遇到了这个问题。我通过启用tcp时间戳来解决它。
根本原因:
关闭连接后,某些连接将进入TIME_WAIT状态 时间。
在此状态下,如果有任何新连接具有相同的IP和PORT, 如果在套接字创建期间未提供SO_REUSEADDR,则套接字bind() 将失败,并显示错误EADDRINUSE。
但是即使提供SO_REUSEADDR之后,也可以对connect()进行sockect 如果双方都未启用tcp时间戳,则失败,并显示错误EADDRNOTAVAIL。
解决方案: 请同时在客户端和服务器上启用tcp时间戳。
回声1> / proc / sys / net / ipv4 / tcp_timestamps
启用tcp_timestamp的原因:
当我们启用tcp_tw_reuse时,处于TIME_WAIT状态的套接字可以在它们到期之前使用,并且内核将尝试确保TCP序列号没有冲突。如果启用tcp_timestamps,将确保不会发生这些冲突。但是,我们需要在两端启用TCP时间戳。有关详细信息,请参见tcp_twsk_unique的定义。
答案 4 :(得分:0)
要检查的另一件事是接口已启动。最近我在使用网络命名空间时对此感到困惑,因为它似乎创建了一个新的网络命名空间,产生了一个完全独立的环回接口但是没有提起它(至少,使用Debian wheezy的版本的东西) 。这让我逃脱了一段时间,因为人们通常不会想到环回因为一直在瘫痪。