2015-01-13 29 views
1

下面是创建套接字连接的代码,如果IP存在,它会返回正套接字描述符,但如果IP不可用,它会卡住在例程connect()中。存在:套接字编程:connect()会挂起一个不存在的IP

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。并且我测试了cout << "conn: " << conn << endl;永远不会被执行,并且如果一个不存在的IP被提供给构造函数,它会一直等待。

这些代码适用于现有的IP。

某处我读过设置socket descriptorO_NONBLOCK会解决悬挂问题。是的,只是出现了一个新问题;我甚至无法连接到现有的IP。

请帮我解释它为什么会发生以及如何解决这个问题。

回答

3

我认为你需要退后一步并考虑IP是否存在是什么意思。

当您拨打connect时,操作系统会发送一个数据包(一个SYN数据包)到目标IP。它不知道IP是否存在。事实上,这个概念没有明确的定义 - 它可能会或可能不会被分配。该设备可能已打开或未插入,可能位于DHCP池中,该池已租用或未租用该IP。操作系统不知道这一点。所有操作系统知道它是否得到答复。而且在任何一个方向都可能出现数据包丢失,从而导致需要回复。

广义上讲,OS可以得到三种答复(你可以用tcpdump或者Wireshark的,看看哪些事情):

  1. 目标与SYN+ACK包IP回复。这是三次握手的下一阶段。目标IP显然正在工作。

  2. 目标IP以RST回复。这意味着'走开';你会看到'连接被拒绝'。

  3. 目标IP或某些中间路由器回复ICMP主机不可达或网络不可达,在这种情况下,您将看到主机不可达或网络不可达。如果主机或网络无法访问,这并不保证会发生。

还有第四种可能性,根本没有收到回复。在这种情况下,connect等待并重试几次,最后超时。这就是你所看到的。在防火墙中过滤掉ICMP会将上面的情况(3)转换为这种情况,但重要的是要注意这可能会发生。所以这是你应该准备处理的一种自然状态。

使用非阻塞connect()(通过首先设置O_NONBLOCK)使得connect()立即返回 - 甚至在功能IP建立连接之前。在任何情况下,您都需要允许某些时间发生连接。通过慢速链接或数据包丢失,功能性IP可能需要数十秒才能连接。因此,在这种情况下,您需要实现自己的超时(例如通过套接字上的select() -ing)。有(在Linux下)无法将自己的超时设置为connect(),所以如果你想改变超时时间,你必须使用非阻塞连接来实现它。来自斯蒂芬斯的详细信息(一本优秀的书 - 买它)在非阻塞connect()here

+0

感谢您很好地解释行为。对不起,我的小知识。我可以在上面提供的代码中超时阻塞套接字吗?如是。那么如何? – Rohit

+0

是的,你需要使用'select()'来做到这一点。我已经修改了最后一段,并且链接了一个例子。 – abligh

+0

我会尝试使用'select()'。感谢您的宝贵意见:) – Rohit