2013-04-17 28 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