c tcp套接字非阻塞接收超时

c tcp socket non blocking receive timeout

本文关键字:超时 tcp 套接字      更新时间:2023-10-16

正在尝试编写一个客户端,该客户端将尝试在3秒内接收数据。我已经使用下面的代码select实现了连接方法。

//socket creation 
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_stAddress.sin_family           = AF_INET;
  m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchIP);
m_stAddress.sin_port             = htons(iPort);
m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;
//connecting to server
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));
long iMode = 0;
iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stWrite;
FD_ZERO(&stWrite);
FD_SET(m_hSocket, &stWrite);
iResult = select(0, NULL, &stWrite, NULL, &m_stTimeout);            
if((iResult > 0) && (FD_ISSET(m_hSocket, &stWrite)))
  return true;

但我无法用下面的代码计算出我在接收超时时缺少了什么?如果服务器连接断开,它不会等待。它只是从select方法立即返回。此外,我如何编写一个带有套接字发送超时的非阻塞套接字调用。

long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
  while ((iBuffLen-1) > 0)
  {
    int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
    if (iRcvLen == SOCKET_ERROR)
    {
      return false;
    }
    else if (iRcvLen == 0)
    {
      break;
    }
    pchBuff  += iRcvLen;
    iBuffLen -= iRcvLen;
  }
}    

要选择的第一个参数不应为0。select的正确用法可在此处找到:http://developerweb.net/viewtopic.php?id=2933

第一个参数应该是套接字的最大值+1,如果是非阻塞的,则将中断的系统调用考虑在内:

/* Call select() */
do {
   FD_ZERO(&readset);
   FD_SET(socket_fd, &readset);
   result = select(socket_fd + 1, &readset, NULL, NULL, NULL);
} while (result == -1 && errno == EINTR);

这只是您可能还需要timeout参数的示例代码。如果你能得到EINTR,这将使你所需的逻辑复杂化,因为如果你得到EINTR,你必须再次打同样的电话,但还有剩余的时间等待。

我认为对于非阻塞模式,需要检查recv()失败以及超时值。这意味着firstselect()将返回套接字是否准备好接收数据。如果是,它将继续,否则它将休眠,直到select()方法调用行上超时为止。但是,如果在读取循环中由于某些不确定的情况导致接收失败,我们需要手动检查套接字错误和最大超时值。如果套接字错误持续并且超时,我们需要中断它。

我已经完成了非阻塞模式下的接收超时逻辑。如果我错了,请纠正我。

  bool bReturn = true;
  SetNonBlockingMode(true);
  //check whether the socket is ready to receive
  fd_set stRead;
  FD_ZERO(&stRead);
  FD_SET(m_hSocket, &stRead);
  int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
  DWORD dwStartTime = GetTickCount();
  DWORD dwCurrentTime = 0;
  //if socket is not ready this line will be hit after 3 sec timeout and go to the end
  //if it is ready control will go inside the read loop and reads data until data ends or
  //socket error is getting triggered continuously for more than 3 secs.
  if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
  {
    while ((iBuffLen-1) > 0)
    {
      int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
      dwCurrentTime = GetTickCount();
      if ((iRcvLen == SOCKET_ERROR) && ((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000))
      {
        bReturn = false;
        break;
      }
      else if (iRcvLen == 0)
      {
        break;
      }
      pchBuff  += iRcvLen;
      iBuffLen -= iRcvLen;
    }
  }
  SetNonBlockingMode(false);
  return bReturn;