socket异步连接超时异常

       最近我的操作系统自动升级了(win11),升级后发现之前正常工作的底层socket代码异常了。经过排查发现异常出在两处

1.使用异步connect-select方式实现的连接超时功能不能正常工作了,但使用setSocketOpt设置接收超时的功能正常了。

2.socket的recv函数偶尔会返回-1,但错误码为0的错误。但不影响继续接收。

    int async_connect (int fd, struct sockaddr* addr, socklen_t len,int timeout=1)
    {
        unsigned long flag = 1;
        if(ioctlsocket((SOCKET)fd, FIONBIO, &flag) != 0)return -1;

        int result =connect(fd, addr, len);
        if(result == 0) return true;/* connection sucess */
        else if(result < 0 && errno != EINPROGRESS) return false;/* connection error */

        struct timeval tv;
        fd_set rfdset;
        fd_set wfdset;
        tv.tv_sec = timeout;
        tv.tv_usec = 0;

        FD_ZERO(&rfdset);
        FD_ZERO(&wfdset);
        FD_SET(fd, &rfdset);
        FD_SET(fd, &wfdset);

        /* wait for readiness */
        int res = select(fd, &rfdset, &wfdset, NULL, &tv);
        if(0 <= res)return -1;

        flag = 0;
        if(ioctlsocket((SOCKET)fd, FIONBIO, &flag) != 0)return -1;

        return 0;
    }

      以上这段代码是出问题的代码,这段代码中connect返回了-1,并且select 函数也会返回-1导致功能无法正确实现。

    bool CSocket::setRecvTimeout(int msTimeout)
    {
        timeval  tv;
        settimeval(&tv, msTimeout);

        if(::setsockopt(m_fd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&tv,sizeof(timeval)) < 0)
        {
            return false;
        }

        return true;
    }
     this->setRecvTimeout(1200);
     if(::connect(m_fd, (struct sockaddr*)&clientsock_in, sizeof(struct sockaddr)) == -1)
     {
        this->close();
        qDebug() << " socket connect fail!";
        return false;
     }

       使用设置setsockopt方式实现的连接超时功能却正常了(之前设置后没有效果)。

	int CSocket::read(char * buffer, int maxSize, int flag)
	{
#ifdef ENABLE_LOG
		QLogHold hold("read...");
#endif
		if (STATE_NORMAL == m_state)
		{
			if (FD_PEEK_MSG_FLAG == flag)
			{
#ifdef _WIN32
				flag = 2;
#else
				flag = MSG_PEEK;
#endif
			}
			else flag = 0;

			int cnt = recv(m_fd, buffer, maxSize, flag);
			m_activeEvent = EVENT_NONE;
			if (cnt > 0)return cnt;
			else if (cnt == 0)
			{
				m_state = STATE_CLOSED;
				return cnt;
			}
			else if ((cnt < 0) && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) //这几种错误码,认为连接是正常的,继续接收
			{
				return cnt;
			}
			else if(errno == ENOENT)
			{
				m_state = STATE_CLOSED;
				return cnt;
			}
			else
			{
                //m_state = STATE_ERROR;
				return cnt;
			}
		}

		return -1;
	}

     这一段是出问题的read代码,问题就在于recv偶发性的会返回-1,但大多时候errno为0,偶尔会是22,经几番测试和验证发现这种情况下返回-1后不影响再次接收,使用while循环接收数据。所以代码实现改为如下:


	int CSocket::read(char * buffer, int maxSize, int flag)
	{
#ifdef ENABLE_LOG
		QLogHold hold("read...");
#endif
		if (STATE_NORMAL == m_state)
		{
			if (FD_PEEK_MSG_FLAG == flag)
			{
#ifdef _WIN32
				flag = 2;
#else
				flag = MSG_PEEK;
#endif
			}
			else flag = 0;

            /*在使用过程中发现,在win11系统上recv偶尔会返回-1,但errno为0,偶尔也会返回位置类型的错误,
              但这些错误似乎不影响其再次接收,所以使用一个while循环处理已知可返回的错误,可再次接收的错误
              进入下一次循环再次接收。*/
            while(1)
            {
                int cnt = recv(m_fd, buffer, maxSize, flag);
                m_activeEvent = EVENT_NONE;
                if (cnt > 0)
                {
                    //正常接收,返回接收的字节数量
                    return cnt;
                }
                else if (cnt == 0)
                {
                    //socket 被远端关闭了
                    m_state = STATE_CLOSED;
                    return cnt;
                }
                else if(errno == ENOENT)
                {
                    m_state = STATE_CLOSED;
                    return cnt;
                }
                //注:除以上的错误代码,其余的情况可以继续接收。
            }
		}

		return -1;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wolfseek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值