2013-04-17 30 views
0

我目前正在嘗試向打開tcp連接的方法添加超時。我大致使用指南發現here除了我試圖使用poll()而不是選擇。但是,我立即調用poll()返回通知fd已準備好寫入,儘管連接未打開。下面是一個減小的代碼示例我公司生產:使用輪詢的TCP超時

#include <cstring> 
#include <poll.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <cerrno> 
#include <iostream> 
#include <cstdlib> 

int main() { 
    struct addrinfo hints; 

    ::memset(&hints, 0, sizeof(hints)); 
    hints.ai_socktype = 0; 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_protocol = 0; 
    hints.ai_flags = AI_CANONNAME; 


    struct addrinfo * info; 

    if(::getaddrinfo("127.0.0.1", "49999", &hints, &info) != 0) { 
     std::cerr << "Error: getaddrinfo" << std::endl; 
     exit(1); 
    } 


    int soc; 

    if((soc = ::socket(info->ai_family, info->ai_socktype, info->ai_protocol)) < 0) { 
     std::cerr << "Erorr: socket" << std::endl; 
    } 

    // Set mode to non-blocking for timeout handling 
    int arg; 
    if((arg = ::fcntl(soc, F_GETFL, 0)) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    arg |= O_NONBLOCK; 
    if(::fcntl(soc, F_SETFL, arg) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    int res = ::connect(soc,info->ai_addr,info->ai_addrlen); 

    if((res != 0) && (errno != EINPROGRESS)) { 
     std::cerr << "Error: connect" << std::endl; 
     exit(1); 
    } 

    pollfd pfd; 
    pfd.fd = soc; 
    pfd.events = POLLOUT; 

    res = ::poll(&pfd, 1, 50000); 

    if(res < 0) { 
     std::cerr << "Error: poll" << std::endl; 
     exit(1); 
    } 
    else if(res == 0) { 
     std::cerr << "Error: poll" << std::endl; 
     exit(1); 
    } 

    // Set blocking mode again 
    if((arg = ::fcntl(soc, F_GETFL, NULL)) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 
    arg &= (~O_NONBLOCK); 
    if(::fcntl(soc, F_SETFL, arg) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    return 0; 
} 

由於端口49999是我的機器上封閉我期望的誤差。程序結束後返回值爲0

我也嘗試使用上面鏈接中的選擇示例。如果我更換呼叫poll()與完整的例子,我得到了以下錯誤消息:

Operation now in progress: Operation now in progres

我試圖減少使用選擇的代碼,但是當我降低它,我得到一個正確的connection refused message

編輯

注:「正在進行操作」的消息,是由於我這邊的錯誤處理代碼中的錯誤。一旦我糾正了這一點,我從「getsockopt()」得到了正確的錯誤信息。這也解釋了爲什麼我無法減少這個例子。

回答

0

問題在於調用丟失getsockopt()調用。如果connect()不成功,我假設poll()會返回一個錯誤,但只要狀態可以確定,它就會通過。一旦我添加了來自示例的getsockopt()調用,它的工作就很好。

下面是使用輪詢一些(不那麼幹淨)示例代碼:

#include <cstring> 
#include <poll.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <cerrno> 
#include <iostream> 
#include <cstdlib> 
#include <cstdio> 

int main() { 
    struct addrinfo hints; 

    ::memset(&hints, 0, sizeof(hints)); 
    hints.ai_socktype = 0; 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_protocol = 0; 
    hints.ai_flags = AI_CANONNAME; 


    struct addrinfo * info; 

    if(::getaddrinfo("127.0.0.1", "49999", &hints, &info) != 0) { 
     std::cerr << "Error: getaddrinfo" << std::endl; 
     exit(1); 
    } 


    int soc; 

    if((soc = ::socket(info->ai_family, info->ai_socktype, info->ai_protocol)) < 0) { 
     std::cerr << "Erorr: socket" << std::endl; 
    } 

    // Set mode to non-blocking for timeout handling 
    int arg; 
    if((arg = ::fcntl(soc, F_GETFL, 0)) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    arg |= O_NONBLOCK; 
    if(::fcntl(soc, F_SETFL, arg) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    int res = ::connect(soc,info->ai_addr,info->ai_addrlen); 

    if(res < 0) { 
     if(errno == EINPROGRESS) { 
     pollfd pfd; 
     pfd.fd = soc; 
     pfd.events = POLLOUT; 

     std::cout << "Polling" << std::endl; 
     res = ::poll(&pfd, 1, 50000); 

     if(res < 0) { 
      std::cerr << "Error: poll" << std::endl; 
      exit(1); 
     } 
     else if(res == 0) { 
      std::cerr << "Error: poll" << std::endl; 
      exit(1); 
     } else { 
      socklen_t lon = sizeof(int); 
      int valopt; 
      if (getsockopt(soc, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) { 
       fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno)); 
       exit(0); 
      } 
      // Check the value returned... 
      if (valopt) { 
       fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)); 
       exit(0); 
      } 
     } 
     } 
     else { 
     std::cerr << "Error: connect" << std::endl; 
     exit(1); 
     } 
    } 

    // Set blocking mode again 
    if((arg = ::fcntl(soc, F_GETFL, NULL)) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 
    arg &= (~O_NONBLOCK); 
    if(::fcntl(soc, F_SETFL, arg) < 0) { 
     std::cerr << "Error: fcntl" << std::endl; 
     exit(1); 
    } 

    return 0; 
} 
+0

「我假設poll()會返回一個錯誤,如果connect()不成功」。假設你錯了。 'connect()'除了阻塞之外什麼都不做,直到套接字變爲可寫,或者超時過期。它是'connect()',如果失敗則返回一個錯誤,並且你的代碼沒有正確地檢查它。如果connect()返回了'EAGAIN',你現在只能通過輪詢修復它;換句話說,你已經實現了我的答案。無論你是否意識到。 – EJP

+0

是的,我注意到我錯誤地認爲它會返回一個錯誤。但是'EINPROGRESS'的缺失檢查並不是我困惑的問題。在我以後的測試中,代碼總是到達'fprintf(stderr,「延遲連接()%d - %s \ n」錯誤,valopt,strerror(valopt));'所以只需添加'EINPROGRESS ''不會做任何事情,因爲它使用'getsockopt()'缺少的檢查(或者在另一種情況下該線路有故障)。你仍然警告過我一個潛在的未來錯誤,這可能會導致更多的後續調試問題。 – LiKao

1

如果連接沒有成功,您只需要進行輪詢。連接到「本地主機」時,它通常會立即成功。

+0

好吧,這可能是一兩件事,我可能要改變,但不是問題的根源。連接沒有成功(因爲端口在本地主機上關閉,所以不能連接)。問題在於民意調查通過並返回一個fd,儘管根本沒有聯繫。 – LiKao