最近我的操作系统自动升级了(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;
}

1172

被折叠的 条评论
为什么被折叠?



