首页 > 解决方案 > 为 connect() winsock2 设置固定超时

问题描述

我正在为客户端-服务器通信基础设施编写一个 C 程序。

我的基本目标是允许连接试验至少在超时前持续 15 秒。这意味着,在连接到服务器时,我希望客户端最多等待 15 秒,以便服务器响应并接受此连接试验。在没有成功连接的 15 秒后,我想返回超时指示。

到目前为止,我能够使用 ioctlsocket() 使套接字非阻塞,然后使用 select() 等待 15 秒。我一直在使用 Remy Lebeau 的功能:

Winsock C++ 连接超时

但是,我的问题是在这 15 秒内尝试从客户端连接到服务器时,服务器尚未打开。我尝试在客户端尝试连接服务器时而不是之前(在 15 秒内)打开服务器,但似乎“exceptfds”集首先被“发出信号”并扰乱了分配给超时的时间。

如果我不使用“exceptfds”,而只使用“writefds”,我可以等待 15 秒超时,但在 15 秒内无法连接到服务器 - 所以我只是遇到超时期间,不能用它来连接。

我已经在某处读到 select() 即使已达到超时也会返回,如果其中一组是“已发出信号” - 有没有办法克服这个问题?我也不想使用任何时间库。

这是我的代码:

non_blocking_ret_val connect_non_blocking(SOCKET sock, const char *ip_addr, u_short port_num){

int ret = 0;

SOCKADDR_IN server = { 0 };
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(ip_addr);
server.sin_port = htons(port_num);

// ipaddr valid?
if (server.sin_addr.s_addr == INADDR_NONE) {
    printf_s("Error in converting ip address. Terminating...\n");
    return CONNECTION_ERROR;
}

// put socket in non-blocking mode...
if (SOCKET_ERROR == set_blocking(sock, FALSE))
    return SOCKET_ERROR;

if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
{
    if (WSAGetLastError() != WSAEWOULDBLOCK)
    {
        // connection failed
        //close_socket_and_deinit_wsa(sock);
        printf_s("Making the connect function a non-blocking one failed, %ld. Terminating...\n", WSAGetLastError());
        return CONNECTION_ERROR;
    }

    // connection pending

    fd_set setW, setE;

    FD_ZERO(&setW);
    FD_SET(sock, &setW);
    FD_ZERO(&setE);
    FD_SET(sock, &setE);

    TIMEVAL time_out = { 0 };
    time_out.tv_sec = CLIENT_CONNECT_TIMEOUT;
    time_out.tv_usec = 0;

    ret = select(0, NULL, &setW, &setE, &time_out);
    if (ret <= 0)
    {
        // select() failed or connection timed out
        if (ret == 0) {
            WSASetLastError(WSAETIMEDOUT);
            return CONNECTION_TIMEOUT;
        }
        //close_socket(sock);
        printf_s("select() function has unexpectedly failed. Terminating...\n");
        return CONNECTION_ERROR;
    }

    if (FD_ISSET(sock, &setE))
    {
        // connection failed
        int err = 0;
        getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, sizeof(err));
        WSASetLastError(err);
        printf_s("Connection to server has failed. Terminating...\n");
        return CONNECTION_FAILED;
    }
}

// connection successful

// put socket in blocking mode...
if (SOCKET_ERROR == set_blocking(sock, TRUE))
    return CONNECTION_ERROR;

return CONNECTION_SUCCEEDED;

}

int set_blocking(SOCKET m_socket, BOOL is_blocking){
unsigned long non_blocking = 1;
unsigned long blocking = 0;
int result = ioctlsocket(m_socket, FIONBIO, is_blocking ? &blocking : &non_blocking);
if (result == SOCKET_ERROR)
    printf_s("Error no.%ld in using ioctlsocket(). Terminating...\n", WSAGetLastError());
return result;

}

谢谢!

标签: cclient-server

解决方案


推荐阅读