2011-08-17 120 views
1

我的問題是以下內容: 我正在編程接口在Linux中通過以太網控制GPIB控制器。爲此,我打開一個TCP套接字並將這些命令發送給Controller。到目前爲止,這工作得很好。我在爲我的接口編寫某種單元測試時發生了這個問題: 要檢查我是否在單獨的線程中使用來自boost lib的tcp接受器,並且只是連接到它而不是實際的控制器。這也起作用,但只要來自接口的connect()調用被阻塞即可。然而,因爲我需要爲連接指定的超時()調用我不得不用select()函數連接:Linux的TCP連接與選擇()失敗在測試服務器

// Open TCP Socket 
    m_Socket = socket(PF_INET,SOCK_STREAM,0); 
    if(m_Socket < 0) 
    { 
     m_connectionStatus = STATUS_CLOSED; 
     return ERR_NET_SOCKET; 
    } 

    struct sockaddr_in addr; 
    inet_aton(m_Host.c_str(), &addr.sin_addr); 
    addr.sin_port = htons(m_Port); 
    addr.sin_family = PF_INET; 

    // Set timeout values for socket 
    struct timeval timeouts; 
    timeouts.tv_sec = SOCKET_TIMEOUT_SEC ; // const -> 5 
    timeouts.tv_usec = SOCKET_TIMEOUT_USEC ; // const -> 0 
    uint8_t optlen = sizeof(timeouts); 

    if(setsockopt(m_Socket, SOL_SOCKET, SO_RCVTIMEO,&timeouts,(socklen_t)optlen) < 0) 
    { 
     m_connectionStatus = STATUS_CLOSED; 
     return ERR_NET_SOCKET; 
    } 

    // Set the Socket to TCP Nodelay (Send immediatly after a send/write command) 
    int flag_TCP_nodelay = 1; 
    if ((setsockopt(m_Socket, IPPROTO_TCP, TCP_NODELAY, 
      (char *)&flag_TCP_nodelay, sizeof(flag_TCP_nodelay))) < 0) 
    { 
     m_connectionStatus = STATUS_CLOSED; 
     return ERR_NET_SOCKET; 
    } 
    // Save Socket Flags 
    int opts_blocking = fcntl(m_Socket, F_GETFL); 
    if (opts_blocking < 0) 
    { 
     return ERR_NET_SOCKET; 
    } 
    int opts_noblocking = (opts_blocking | O_NONBLOCK); 
    // Set Socket to Non-Blocking 
    if (fcntl(m_Socket, F_SETFL, opts_noblocking)<0) 
    { 
     return ERR_NET_SOCKET; 
    } 
    // Connect 
    if (connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
    { 
     // EINPROGRESS always appears on Non Blocking connect 
     if (errno != EINPROGRESS) 
     { 
      m_connectionStatus = STATUS_CLOSED; 
      return ERR_NET_SOCKET; 
     } 
     // Create a set of sockets for select 
     fd_set socks; 
     FD_ZERO(&socks); 
     FD_SET(m_Socket,&socks); 
     // Wait for connection or timeout 
     int fdcnt = select(m_Socket+1,NULL,&socks,NULL,&timeouts); 
     if (fdcnt < 0) 
     { 
      return ERR_NET_SOCKET; 
     } 
     else if (fdcnt == 0) 
     { 
      return ERR_TIMEOUT; 
     } 
    } 
    //Set Socket to Blocking again 
    if(fcntl(m_Socket,F_SETFL,opts_blocking)<0) 
    { 
     return ERR_NET_SOCKET; 
    } 

    m_connectionStatus = STATUS_OPEN; 
    return x2e::OK; 

如果我用這個功能,我仍然可以連接真實控制器上,並與它進行通信。但是,如果我使用我的測試服務器,我只是無法連接,只選擇返回值爲0的葉子。 因此,現在有人可能會說我的測試服務器不工作....但如果我使用阻塞連接()打電話我可以發送到我的測試服務器沒有任何問題... 也許有人有一個想法我可以做什麼......?

+0

的零的返回值表示超時。這可能會也可能不是問題,但您似乎並未在發佈的摘錄中設置超時結構。引用手冊頁「在Linux上,select()修改超時以反映未睡眠的時間量......考慮超時在select()返回後未定義。」如果您在不重置值的情況下再次調用它,會導致問題。 –

+0

對不起我的錯,我忘了將timeouts結構的定義添加到我的代碼中!要解決這個問題......但這也意味着這不是問題:/ /所以現在的代碼包含處理連接的整個功能 – Toby

+0

你是否知道你沒有關閉任何地方的插座?你沒有重複使用它,使用相同的套接字多次連接,你呢? – rodrigo

回答

2

與非阻塞套接字connect()調用可以與連接返回0,是還沒有準備好 的connect()代碼段,可以這樣寫(我的連接wraper代碼段從Python實現學):

if (FAIL_CHECK(connect(sock, (struct sockaddr *) &channel, sizeof(channel)) && 
      errno != EINPROGRESS)) 
    { 
     gko_log(WARNING, "connect error"); 
     ret = HOST_DOWN_FAIL; 
     goto CONNECT_END; 
    } 

    /** Wait for write bit to be set **/ 
#if HAVE_POLL 
    { 
     struct pollfd pollfd; 

     pollfd.fd = sock; 
     pollfd.events = POLLOUT; 

     /* send_sec is in seconds, timeout in ms */ 
     select_ret = poll(&pollfd, 1, (int)(send_sec * 1000 + 1)); 
    } 
#else 
    { 
     FD_ZERO(&wset); 
     FD_SET(sock, &wset); 
     select_ret = select(sock + 1, 0, &wset, 0, &send_timeout); 
    } 
#endif /* HAVE_POLL */ 
    if (select_ret < 0) 
    { 
     gko_log(FATAL, "select/poll error on connect"); 
     ret = HOST_DOWN_FAIL; 
     goto CONNECT_END; 
    } 
    if (!select_ret) 
    { 
     gko_log(FATAL, "connect timeout on connect"); 
     ret = HOST_DOWN_FAIL; 
     goto CONNECT_END; 
    } 

蟒版本代碼段:

res = connect(s->sock_fd, addr, addrlen); 
if (s->sock_timeout > 0.0) { 
    if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) { 
     timeout = internal_select(s, 1); 
     if (timeout == 0) { 
      /* Bug #1019808: in case of an EINPROGRESS, 
       use getsockopt(SO_ERROR) to get the real 
       error. */ 
      socklen_t res_size = sizeof res; 
      (void)getsockopt(s->sock_fd, SOL_SOCKET, 
          SO_ERROR, &res, &res_size); 
      if (res == EISCONN) 
       res = 0; 
      errno = res; 
     } 
     else if (timeout == -1) { 
      res = errno;   /* had error */ 
     } 
     else 
      res = EWOULDBLOCK;      /* timed out */ 
    } 
} 

if (res < 0) 
    res = errno;