C++ 为什么connect()会给出EADDRNOTAVAIL?
在我的申请中,出现了一个似乎不可复制的失败。我有一个TCP套接字连接失败,应用程序试图重新连接它。在第二次调用connect()尝试重新连接时,我得到了一个errno==EADDRNOTAVAIL的错误结果,connect()的手册页表示:“指定的地址在本地计算机上不可用。” 查看对connect()的调用,第二个参数似乎是错误引用的地址,但据我所知,这个参数是远程主机的TCP套接字地址,因此我对引用本地计算机的手册页感到困惑。远程TCP套接字主机的此地址是否无法从本地计算机获得?如果是,为什么会这样?它必须在连接失败之前第一次成功调用connect(),然后尝试重新连接并出现此错误。connect()的参数两次都相同 如果我再次尝试调用connect,如果我等待足够长的时间,这个错误会不会是暂时的?如果没有,我应该如何尝试从此故障中恢复?检查此链接 编辑:是的,我本想添加更多,但由于紧急情况,不得不在那里删掉 在尝试重新连接之前是否关闭了插座?关闭将告诉系统socketpair(ip/端口)现在可用 这里还有其他项目,请看:C++ 为什么connect()会给出EADDRNOTAVAIL?,c++,linux,sockets,tcp,ip-address,C++,Linux,Sockets,Tcp,Ip Address,在我的申请中,出现了一个似乎不可复制的失败。我有一个TCP套接字连接失败,应用程序试图重新连接它。在第二次调用connect()尝试重新连接时,我得到了一个errno==EADDRNOTAVAIL的错误结果,connect()的手册页表示:“指定的地址在本地计算机上不可用。” 查看对connect()的调用,第二个参数似乎是错误引用的地址,但据我所知,这个参数是远程主机的TCP套接字地址,因此我对引用本地计算机的手册页感到困惑。远程TCP套接字主机的此地址是否无法从本地计算机获得?如果是,为什么
- 如果本地端口已经连接到给定的远程IP和端口(即,已经有相同的socketpair),您将收到此错误(请参阅下面的错误链接)
- 绑定非本地套接字地址将产生此错误。如果一台机器的IP地址是127.0.0.1和1.2.3.4,并且您试图绑定到1.2.3.5,则会出现此错误
- EADDRNOTAVAIL:指定的地址在远程计算机上不可用,或者名称结构的地址字段为全零
您的套接字似乎基本上处于TCP内部状态之一,为重新连接添加延迟可能会解决您的问题,就像他们在错误报告中所做的那样。如果提供了无效端口(如0),也会发生这种情况。另一件需要检查的事情是接口是否已启动。最近在使用网络名称空间时,我被这一点弄糊涂了,因为创建一个新的网络名称空间似乎会产生一个完全独立的环回接口,但并没有带来它(至少在Debian wheezy的版本中是这样)。有一段时间我没有意识到这一点,因为人们通常认为环回不会停止。如果您不愿意更改可用的临时端口数(如David所建议的),或者您需要的连接数超过理论上的最大值,那么还有两种方法可以减少正在使用的端口数。但是,它们在不同程度上违反了TCP标准,因此应谨慎使用 第一种方法是打开
SO_LINGER
,超时时间为零秒,强制TCP
堆栈发送RST数据包并刷新连接状态。然而,有一个微妙之处:在close
之前,您应该在套接字文件描述符上调用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);
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));
只有当FIN
数据包与RST
数据包重新排序时,服务器才会看到过早的连接重置
有关更多详细信息,请参阅。(从实验上看,在哪里设置setsockopt
,似乎并不重要)
第二种方法是使用SO\u REUSEADDR
和显式的bind
(即使您是客户机),这将允许Linux在运行时重用临时端口,而不必等待它们。请注意,您必须将bind
与INADDR\u ANY
和port0
一起使用,否则将不受尊重。您的代码将类似于:
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);
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连接的内部内核数据结构。但是,在发生这种情况之前,您不会开始重用端口。我遇到了这个问题。我通过启用tcp时间戳解决了这个问题
根本原因:
连接关闭后,连接将进入TIME\u WAIT状态一段时间
时间
在此状态下,如果任何新连接具有相同的IP和端口,
如果在套接字创建过程中没有提供sou REUSEADDR,那么套接字绑定()
将失败并出现错误
但即使在提供了SO_REUSEADDR之后,Socket connect()也可能
如果两侧未启用tcp时间戳,则失败并出现错误EADDRNOTAVAIL
解决方案:
请在客户端和服务器端启用tcp时间戳
echo 1>/proc/sys/net/ipv4/tcp\u时间戳
启用tcp_时间戳的原因:
当我们启用tcp_tw_重用时,处于TIME_WAIT状态的套接字可以在过期之前使用,内核将尝试确保tcp序列号没有冲突。如果我们启用tcp_时间戳,它将确保这些冲突不会发生。但是,我们需要在两端启用TCP时间戳。有关详细信息,请参见tcp_twsk_unique的定义
参考:
我在一个大型Redis集群中遇到了类似的问题。您的用例是什么?作为目标端口。如果它是作为要绑定的本地端口提供的,那么它是有效的。将SO_LINGER
设置为零解决了我的问题。非常感谢。