2015-01-13 31 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