c tcp socket非阻塞接收超时

时间:2015-04-08 17:04:46

标签: c++ c sockets

尝试编写一个尝试接收数据直到3秒的客户端。我使用下面的代码选择实现了connect方法。

//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;
  }
}    

2 个答案:

答案 0 :(得分:1)

要选择的第一个参数不应为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,你必须再次做同样的电话,但剩下的时间要等。

答案 1 :(得分:0)

我认为对于非阻塞模式,需要检查recv()失败以及超时值。这意味着第一个select()将返回套接字是否准备好接收数据。如果是,它将继续前进,否则它将一直睡眠,直到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;