套接字编程:connect() 因不存在的 IP 而挂起
socket programming: connect() hangs for a non-existent IP
下面是创建套接字连接的代码,如果 IP 存在,returns 正套接字描述符,如果 IP 不存在,它会卡在例程中connect()
:
Connection::Connection(string& ip) : sock(0), status(0), conn(0){
struct sockaddr_in sin;
sock = socket(AF_INET, SOCK_STREAM, 0);//socket() returns -1 on failure.
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = inet_addr(ip.c_str());
cout << "sock: " << sock << endl;
//fcntl(sock, F_SETFL, O_NONBLOCK);
if(sock != -1){
conn = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
cout << "conn: " << conn << endl;
if ( conn != 0){
status = -2;
}
}
else{
status = -1;
}
}
出于调试目的,我在 socket()
和 connect()
之后放置了 cout
。而且我已经测试过,如果向构造函数提供了不存在的 IP,cout << "conn: " << conn << endl;
永远不会执行并继续等待。
这些代码适用于现有 IP。
我曾在某处读到,将 socket descriptor
设置为 O_NONBLOCK
可以解决挂起问题。是的,确实如此,但是出现了一个新问题;我什至无法连接到现有 IP。
请帮我解释为什么会这样以及如何解决这个问题。
我认为您需要退后一步,考虑一下您所说的 IP 'exists'.
是什么意思
当你调用connect
时,OS会发送一个数据包(一个SYN
数据包)到目标IP。它不知道 IP 'exists' 是否存在。事实上,这个概念没有明确定义 - 它可能会或可能不会被分配。该设备可能会或可能不会打开或插入。它可能位于 DHCP 池中,该 IP 的租约已经或尚未分发。 OS 知道 none 这个。 OS 只知道它是否得到回复。并且两个方向都可能丢包,需要回复
一般来说,OS 可以获得三种类型的回复(您可以使用 tcpdump
或 wireshark 来查看发生了什么):
目标 IP 回复 SYN+ACK
数据包。这是三次握手的下一阶段。目标 IP 显然有效。
目标 IP 回复 RST
。这意味着 'go away';你会看到 'connection refused'.
目标 IP 或某个中间路由器回复 ICMP 主机无法访问或网络无法访问,在这种情况下,您将看到主机无法访问或网络无法访问。如果主机或网络无法访问,则不能保证会发生这种情况。
还有第四种可能,就是根本没有收到回复。本例connect
等待重试几次,最后超时。这就是你所看到的。在防火墙中过滤掉 ICMP 会将上面的情况 (3) 转换为这种情况,但重要的是要注意这无论如何都可能发生。所以这是一个自然的事态,你应该准备好处理。
使用非阻塞connect()
(通过首先设置O_NONBLOCK
)使connect()
return立即-甚至在功能IP建立连接之前。在任何情况下,您都需要等待 一些 时间才能建立连接。在缓慢 link 或数据包丢失的情况下,功能正常的 IP 可能需要数十秒才能连接。因此,在这种情况下,您需要实现自己的超时(例如通过套接字上的 select()
-ing)。没有(无论如何在 Linux 下)将您自己的超时设置为 connect()
的选项,因此如果您想更改超时,您必须使用非阻塞连接来实现它。 Stephens(一本优秀的书 - 购买)关于非阻塞的详细信息 connect()
here.
下面是创建套接字连接的代码,如果 IP 存在,returns 正套接字描述符,如果 IP 不存在,它会卡在例程中connect()
:
Connection::Connection(string& ip) : sock(0), status(0), conn(0){
struct sockaddr_in sin;
sock = socket(AF_INET, SOCK_STREAM, 0);//socket() returns -1 on failure.
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = inet_addr(ip.c_str());
cout << "sock: " << sock << endl;
//fcntl(sock, F_SETFL, O_NONBLOCK);
if(sock != -1){
conn = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
cout << "conn: " << conn << endl;
if ( conn != 0){
status = -2;
}
}
else{
status = -1;
}
}
出于调试目的,我在 socket()
和 connect()
之后放置了 cout
。而且我已经测试过,如果向构造函数提供了不存在的 IP,cout << "conn: " << conn << endl;
永远不会执行并继续等待。
这些代码适用于现有 IP。
我曾在某处读到,将 socket descriptor
设置为 O_NONBLOCK
可以解决挂起问题。是的,确实如此,但是出现了一个新问题;我什至无法连接到现有 IP。
请帮我解释为什么会这样以及如何解决这个问题。
我认为您需要退后一步,考虑一下您所说的 IP 'exists'.
是什么意思当你调用connect
时,OS会发送一个数据包(一个SYN
数据包)到目标IP。它不知道 IP 'exists' 是否存在。事实上,这个概念没有明确定义 - 它可能会或可能不会被分配。该设备可能会或可能不会打开或插入。它可能位于 DHCP 池中,该 IP 的租约已经或尚未分发。 OS 知道 none 这个。 OS 只知道它是否得到回复。并且两个方向都可能丢包,需要回复
一般来说,OS 可以获得三种类型的回复(您可以使用 tcpdump
或 wireshark 来查看发生了什么):
目标 IP 回复
SYN+ACK
数据包。这是三次握手的下一阶段。目标 IP 显然有效。目标 IP 回复
RST
。这意味着 'go away';你会看到 'connection refused'.目标 IP 或某个中间路由器回复 ICMP 主机无法访问或网络无法访问,在这种情况下,您将看到主机无法访问或网络无法访问。如果主机或网络无法访问,则不能保证会发生这种情况。
还有第四种可能,就是根本没有收到回复。本例connect
等待重试几次,最后超时。这就是你所看到的。在防火墙中过滤掉 ICMP 会将上面的情况 (3) 转换为这种情况,但重要的是要注意这无论如何都可能发生。所以这是一个自然的事态,你应该准备好处理。
使用非阻塞connect()
(通过首先设置O_NONBLOCK
)使connect()
return立即-甚至在功能IP建立连接之前。在任何情况下,您都需要等待 一些 时间才能建立连接。在缓慢 link 或数据包丢失的情况下,功能正常的 IP 可能需要数十秒才能连接。因此,在这种情况下,您需要实现自己的超时(例如通过套接字上的 select()
-ing)。没有(无论如何在 Linux 下)将您自己的超时设置为 connect()
的选项,因此如果您想更改超时,您必须使用非阻塞连接来实现它。 Stephens(一本优秀的书 - 购买)关于非阻塞的详细信息 connect()
here.