套接字编程:connect()为不存在的IP挂起

时间:2015-01-13 07:17:19

标签: c++ sockets network-programming

下面是创建套接字连接的代码,如果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;
    }
}

出于调试目的,我在coutsocket()之后添加了connect()。我已经测试过cout << "conn: " << conn << endl;永远不会被执行并继续等待,如果为构造函数提供了不存在的IP。

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

我曾在某处读到将socket descriptor设置为O_NONBLOCK可以解决悬而未决的问题。是的,确实如此,但是出现了一个新问题;我甚至无法连接到现有的IP。

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

1 个答案:

答案 0 :(得分:4)

我认为您需要退后一步,考虑IP是否“存在”是什么意思。

当您呼叫connect时,操作系统会将数据包(SYN数据包)发送到目标IP。它不知道IP是否“存在”。实际上,这个概念没有明确定义 - 它可能会也可能不会被分配。设备可能打开也可能没有打开或插入。它可能位于DHCP池中,该IP已经或尚未分发。操作系统不知道这一点。所有操作系统都知道它是否得到回复。并且可能在任一方向都有丢包,这使得有必要回复。

从广义上讲,操作系统可以获得三种类型的回复(您可以使用tcpdump或wireshark查看发生的情况):

  1. 目标IP使用SYN+ACK数据包进行回复。这是三次握手的下一个阶段。目的地IP显然正在运作。

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

  3. 目标IP或某个中间路由器回复ICMP主机无法访问或网络无法访问,在这种情况下,您将看到主机无法访问或网络无法访问。如果无法访问主机或网络,则无法保证这一点。

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

    使用非阻止connect()(首先设置O_NONBLOCK)会使connect()立即返回 - 甚至在功能IP建立连接之前。在任何情况下,您都需要允许一些时间来进行连接。通过慢速链路或丢包,功能IP可能需要几十秒才能连接。因此,在这种情况下,您需要实现自己的超时(例如,通过select() - 在套接字上)。有(在Linux下无论如何)没有选项可以将自己的超时设置为connect(),因此如果要更改超时,则必须使用非阻塞连接来实现它。来自Stephens的详细信息(一本优秀的书 - 购买它)非阻塞connect() here