【问题标题】:Linux TCP connect with Select() fails at testserverLinux TCP 与 Select() 连接在 testserver 失败
【发布时间】:2011-08-17 07:16:27
【问题描述】:

我的问题如下: 我正在 Linux 中编写接口以通过以太网控制 GPIB 控制器。为此,我打开一个 TCP 套接字并将命令发送到控制器。到目前为止,这工作正常。我在为我的接口编写某种单元测试时遇到的问题: 要检查我是否在单独的线程中使用来自 boost lib 的 tcp 接受器,只需连接到它而不是实际的控制器。这也有效,但前提是来自接口的 connect() 调用被阻塞。但是,由于我需要为 connect() 调用指定超时时间,所以我必须使用 select() 函数进行连接:

    // 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。 所以现在有人可能会说我的测试服务器无法正常工作......但是如果我使用阻塞的 connect() 调用,我可以毫无问题地发送到我的测试服务器...... 也许有人知道我能做什么......?

【问题讨论】:

  • 返回值为零表示超时。这可能是也可能不是问题,但您似乎没有在发布的摘录中设置超时结构。引用手册页“在 Linux 上,select() 修改超时以反映未睡眠的时间量......在 select() 返回后考虑超时未定义。”如果您在不重置值的情况下再次调用它会导致问题。
  • 对不起我的错,我忘了在我的代码中添加超时结构的定义!!!会解决这个问题......但这也意味着这不是问题://所以现在代码包含处理连接的整个函数
  • 你知道你没有在任何地方关闭套接字吗?你没有重用它,用同一个套接字多次调用连接,是吗?
  • 我刚刚发布的代码是全局“连接函数”的一部分,它实际上不应实现套接字的关闭。稍后将通过另一个函数调用关闭套接字,但在此之前我想做一些通信。
  • 但请注意,如果任何套接字功能失败,您将无法可靠地再次使用它,您必须关闭它并创建一个新的。

标签: linux sockets select boost tcp


【解决方案1】:

使用非阻塞套接字 connect() 调用可能会返回 0,但连接仍未准备好 connect() 代码部分,可以这样写(我的连接包装代码段是从 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;
    }

python版本代码段:

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;

【讨论】:

    猜你喜欢
    • 2020-08-03
    • 2013-11-17
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多