非阻塞式Connect

本文介绍了一种在客户端IP变化时避免connect系统调用长时间阻塞的方法,通过将connect设置为非阻塞模式并结合select进行超时控制,解决了因IP变更导致的连接建立延迟问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近遇到阻塞式connect在三步握手过程中,客户端自身IP发生变化时,connect系统调用阻塞时间过长的问题。故将connect修改为非阻塞的方式。

int is_nonblock(int sockfd)
{
    return ((fcntl(sockfd, F_GETFD, 0) & O_NONBLOCK) == O_NONBLOCK) ? TRUE : FALSE;
}

int set_nonblock(int sockfd)
{
    return fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK);
}

int set_block(int sockfd)
{
    return fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) & (~O_NONBLOCK));
}

int connect_nonblock(int sock_fd, struct sockaddr *addr, socklen_t addrlen, int sec)
{
    int is_fd_nonblock = FALSE, ret = 0, error = 0;
    socklen_t len;
    fd_set rset, wset;
    struct timeval tval;

    if (0 > sock_fd || NULL == addr)
    {
        return -1;
    }

    is_fd_nonblock = is_nonblock(sock_fd);

    if (!is_fd_nonblock)
    {
        set_nonblock(sock_fd);
    }

    if ((ret = connect(sock_fd, addr, addrlen)) < 0)
    {
        /* 非阻塞式connect,内核返回错误码为正在执行中 */
        if (EINPROGRESS != errno)
        {
            goto done;
        }
    }
    else if (0 == ret)
    {
        /* connect直接成功 */
        goto done;
    }

    FD_ZERO(&rset);
    FD_ZERO(&wset);
    FD_SET(sock_fd, &rset);
    FD_SET(sock_fd, &wset);
    tval.tv_sec = sec;
    tval.tv_usec = 0;

    /* select监听描述符, 设置超时时间为tval,当被信号异常中断时,重新进入select,新的超时时间为剩余时间 */
    do {
        ret = select(sock_fd + 1, &rset, &wset, NULL, &tval);
    } while((-1 == ret) && (EINTR == errno));

    if (ret == 0)
    {
        /* 超时 */
        errno = ETIMEDOUT;
        ret = -1;
        goto done;
    }
    else if (ret < 0)
    {
        DBG_ERR("select error: %s\n", strerror(errno));
        ret = -1;
        goto done;
    }

    ret = 0;

    if (FD_ISSET(sock_fd, &rset) || FD_ISSET(sock_fd, &wset))
    {
        /* 若connect成功,socket的错误应该为0,否则为connect错误时的errno */
        len = sizeof(error);
        if ((ret = getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len)) < 0)
        {
            ret = -1;
            goto done;
        }
        else
        {
            if (error)
            {
                errno = error;
                ret = -1;
                goto done;
            }
        }
    }
    else
    {
        DBG_ERR("select error: sock_fd not set\n");
    }

done:
    if (!is_fd_nonblock)
    {
        set_block(sock_fd);
    }

    return ret;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值