【问题标题】:socket programming: connect() hangs for a non-existent IP套接字编程:connect() 挂起一个不存在的 IP
【发布时间】:2015-01-13 07:17:18
【问题描述】:

以下是创建套接字连接的代码,如果 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 &lt;&lt; "conn: " &lt;&lt; conn &lt;&lt; endl; 永远不会执行并继续等待,如果提供给构造函数的 IP 不存在。

这些代码适用于现有 IP。

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

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

【问题讨论】:

    标签: c++ sockets network-programming


    【解决方案1】:

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

    当您调用connect 时,操作系统将向目标IP 发送一个数据包(SYN 数据包)。它不知道 IP 是否“存在”。事实上,这个概念没有明确定义——它可能会或可能不会被分配。该设备可能会或可能不会打开或插入。它可能位于 DHCP 池中,该 IP 的租约已分发或尚未分发。操作系统对此一无所知。操作系统只知道它是否得到回复。并且在任一方向都可能存在丢包,因此需要回复。

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

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

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

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

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

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

    【讨论】:

    • 感谢您很好地解释了这种行为。对不起我的小知识。我可以在我提供的上述代码中使阻塞套接字超时吗?如是。那么如何?
    • 是的,您需要使用select() 来执行此操作。我已经修改了最后一段并添加了一个示例链接。
    • 我会尝试使用select()。感谢您的宝贵意见:)
    • 第四个案例的超时时间是多少?
    猜你喜欢
    • 2019-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-27
    相关资源
    最近更新 更多