【问题标题】:c tcp socket non blocking receive timeoutc tcp socket非阻塞接收超时
【发布时间】:2015-04-08 17:04:46
【问题描述】:

尝试编写一个客户端,它将尝试接收数据直到 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;
  }
}    

【问题讨论】:

  • 您没有提供创建套接字的代码,所以我不知道它是什么类型的。尽管您似乎认为可以“断开连接”是有意义的,但是,它必须是某种类型的流套接字。在这种情况下,为什么您的程序在断开连接后还要继续尝试读取呢?在重新连接套接字之前,不会有任何数据出现,这不会自动发生。
  • 我想我一旦收到 SOCKET_ERROR 就退出接收循环,这是错误的。至于单次读取,它可能会返回 SOCKET_ERROR 并且最终对于下一次读取它可能会报告成功,所以我想在这里我需要检查经过的时间。是这样吗?
  • 使用写入和错误集:select(, , , , timeout);

标签: c++ c sockets


【解决方案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);

这只是示例代码,您可能还需要超时参数。 如果您可以获取 EINTR,这将使您所需的逻辑复杂化,因为如果您获取 EINTR,您必须再次执行相同的调用,但剩余时间要等待。

【讨论】:

  • 这里我处理的是单个套接字实例。那么我真的需要考虑(socket_fd + 1)吗?而且我不能这样循环,我需要检查我的接收方法是否未能接收到最大超时限制的数据。
  • 我并不是说您必须按原样使用此代码。但是你应该检查你的返回码,看看它是否有 EINTR,如果有,你需要考虑它,是的,你需要 socketdescriptor +1
  • 如果 EINTR 在图片中,您必须创建一个时间变量并将经过的时间考虑在内。可能会使代码复杂一点。
  • 我想我一旦收到 SOCKET_ERROR 就退出接收循环,这是错误的。至于单次读取,它可能会返回 SOCKET_ERROR 并且最终对于下一次读取它可能会报告成功,所以我想在这里我需要检查经过的时间。是这样吗?如果是这种情况,我不需要使用 select() 来等待超时。只需要将套接字设置为非阻塞模式。我说的对吗?
  • 阻塞比较容易,阻塞模式下无法获取EINTR。
【解决方案2】:

我认为对于非阻塞模式,需要检查 recv() 失败以及超时值。这意味着 first 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;

【讨论】:

  • 有评论 cmets 的人吗?
猜你喜欢
  • 1970-01-01
  • 2013-11-11
  • 1970-01-01
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多