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

socket programming: connect() hangs for a non-existent IP

本文关键字:挂起 不存在 IP 编程 connect 套接字      更新时间:2023-10-16
下面是创建套接字连接的代码,如果 IP 存在,则返回正套接字描述符,

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;
    }
}

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

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

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

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

我认为您需要退后一步,考虑一下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()的选项,因此如果要更改超时,则必须使用非阻塞连接来实现它。斯蒂芬斯(一本很棒的书 - 购买它)关于非阻塞connect()的详细信息在这里。