2013-04-17 2 views
0

현재 TCP 연결을 여는 메소드에 시간 초과를 추가하려고합니다. 나는 대충 선택 가이드 대신 poll()을 사용하려고한다는 것을 제외하고는 here이라는 가이드를 사용하고 있습니다. 그러나 poll()을 호출하면 연결이 열려 있지 않아도 fd가 쓰기 준비가되었음을 알리는 즉시 반환됩니다. 포트 49999 내 컴퓨터에 닫혀폴링을 사용하는 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; 
} 

때문에 나는 오류를 기대 : 여기에 내가 생성 된 감소 코드 예입니다. 대신 프로그램은 0의 반환 값으로 끝납니다.

또한 위의 링크에서 발견 된 선택 예제를 사용하려고했습니다.

Operation now in progress: Operation now in progres

내가 선택 사용하여 코드를 줄이기 위해 노력했다,하지만 난 그것을 줄일 때, 나는 올바른 connection refused message를 얻을 : 나는 전체 예제를 poll()에 전화를 교체 할 경우 나는 다음과 같은 오류 메시지가 나타납니다.

편집 :

참고 : "작업을 현재 진행 중"메시지가 내 옆에 오류 처리 코드의 오류 때문. 이 문제를 해결하면 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

"connect()가 성공하지 못하면 poll()이 오류를 반환한다고 가정합니다." 당신이 잘못했다고 생각했습니다. 'connect()'는 소켓을 쓰기 가능하게하거나 타임 아웃이 끝날 때까지 블럭을 제외하고 아무것도하지 않는다. 실패했을 때 오류를 반환하고 코드가 올바르게 검사하지 않으면'connect()'입니다. 'connect()'가'EAGAIN'을 리턴했다면 폴링만으로이를 고쳤습니다. 다른 말로하면 내 대답을 구현했습니다. 당신이 그것을 깨닫는 지 안하든. – EJP

+0

예, 오류가 반환된다고 가정하는 것은 잘못되었습니다. 그러나 'EINPROGRESS'에 대한 누락 된 체크는 나를 혼란스럽게하는 문제가 아닙니다. 나중의 테스트에서, 코드는 항상'fprintf (stderr, "지연 연결 (% d - % s \ n", valopt, strerror (valopt));에 오류가 발생했습니다. 그래서'EINPROGRESS '는'getsockopt()'를 사용하여 검사를했기 때문에 아무것도하지 않았을 것입니다 (또는 다른 경우에는 그 라인이 잘못되었습니다). 아직도 잠재적 인 미래의 버그에 대해 경고 해 주었고 나중에 버그를 디버깅 할 수있었습니다. – LiKao

1

연결이 성공하지 못한 경우에만 폴링을 수행하면됩니다. "localhost"에 연결할 때 일반적으로 곧바로 성공합니다.

+0

좋아, 즉 내가 변경해야 할 수도 있습니다 하나의 일이 될 수 있지만 문제의 근본 원인 : 여기

는 일부 (그래서 청소하지 않음) 예를 들어 설문 조사를 사용하는 코드입니다. 연결이 성공하지 못했습니다 (포트가 로컬 호스트에서 종료 되었기 때문에 연결할 수 없습니다). 문제는 설문 조사가 진행되고 fd를 반환하는 것과 연결이 전혀없는 경우입니다. – LiKao

관련 문제