下面是创建套接字连接的代码,如果IP存在则返回正套接字描述符,而如果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;
}
}
出于调试目的,我在cout
和socket()
之后添加了connect()
。我已经测试过cout << "conn: " << conn << endl;
永远不会被执行并继续等待,如果为构造函数提供了不存在的IP。
这些代码适用于现有的IP。
我曾在某处读到将socket descriptor
设置为O_NONBLOCK
可以解决悬而未决的问题。是的,确实如此,但是出现了一个新问题;我甚至无法连接到现有的IP。
请帮助我解释它为什么会发生以及如何解决这个问题。
答案 0 :(得分:4)
我认为您需要退后一步,考虑IP是否“存在”是什么意思。
当您呼叫connect
时,操作系统会将数据包(SYN
数据包)发送到目标IP。它不知道IP是否“存在”。实际上,这个概念没有明确定义 - 它可能会也可能不会被分配。设备可能打开也可能没有打开或插入。它可能位于DHCP池中,该IP已经或尚未分发。操作系统不知道这一点。所有操作系统都知道它是否得到回复。并且可能在任一方向都有丢包,这使得有必要回复。
从广义上讲,操作系统可以获得三种类型的回复(您可以使用tcpdump
或wireshark查看发生的情况):
目标IP使用SYN+ACK
数据包进行回复。这是三次握手的下一个阶段。目的地IP显然正在运作。
目标IP使用RST
回复。这意味着'走开';你会看到'连接被拒绝'。
目标IP或某个中间路由器回复ICMP主机无法访问或网络无法访问,在这种情况下,您将看到主机无法访问或网络无法访问。如果无法访问主机或网络,则无法保证这一点。
还有第四种可能性,即根本没有收到回复。在这种情况下,connect
等待并重试几次,最后超时。这就是你所看到的。过滤掉防火墙中的ICMP会将上面的情况(3)转换为这种情况,但重要的是要注意无论如何都会发生这种情况。所以这是你应该准备好处理的自然状态。
使用非阻止connect()
(首先设置O_NONBLOCK
)会使connect()
立即返回 - 甚至在功能IP建立连接之前。在任何情况下,您都需要允许一些时间来进行连接。通过慢速链路或丢包,功能IP可能需要几十秒才能连接。因此,在这种情况下,您需要实现自己的超时(例如,通过select()
- 在套接字上)。有(在Linux下无论如何)没有选项可以将自己的超时设置为connect()
,因此如果要更改超时,则必须使用非阻塞连接来实现它。来自Stephens的详细信息(一本优秀的书 - 购买它)非阻塞connect()
here。