首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TCP套接字重试机制

TCP套接字重试机制
EN

Code Review用户
提问于 2015-04-29 20:06:18
回答 2查看 7.2K关注 0票数 7

我正在编写一个C++客户端应用程序,它将向服务器发送一些数据并等待其响应。现在协议是等待特定的超时,然后重新尝试特定的时间。如果一切出错,客户端将报告通信失败。

我在一个非阻塞套接字操作中实现了整个过程。我对我的发送/接收方法是否正确有一些疑问。

下面是我在Windows平台上用VC++ 2005编写的TCP通信代码。

套接字参数生成方法

代码语言:javascript
复制
bool CTCPCommunication::OpenConnection(bool bRetryConnect)
{
  //create the socket handle and config its paramaters
  m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (m_hSocket == INVALID_SOCKET)
  {
    WRITELOG("Call to API 'socket' failed, error: %d", WSAGetLastError());
    return false;
  }

  //setting socket address
  CT2CA serverIP(m_csServerIP);
  char* pchServerIP = serverIP;
  m_stAddress.sin_family           = AF_INET;
  m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchServerIP);
  m_stAddress.sin_port             = htons(m_iServerPort);

  //setting socket timeout
  m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
  m_stTimeout.tv_usec = 0;

  //set socket to non blocking mode
  unsigned long iMode = 1;
  int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
  if (iResult != NO_ERROR)
  {
    WRITELOG("Call to API 'ioctlsocket' failed, error: %d", WSAGetLastError());
    return false;
  }

  bool bSuccess = false;
  //Called for the first time when starting server connection
  if (bRetryConnect == false)
  {
    bSuccess = InitialConnect();
  }
  //For all the other time when client detects a failure in communication and makes a retry
  else
  {
    bSuccess = Connect();
  }

  return bSuccess;
}

连接构建方法

代码语言:javascript
复制
bool CTCPCommunication::Connect()
{
  ReportStatus(App_Stat_Connect);
  CT2CA serverIP(m_csServerIP);
  char* pchserverIP = serverIP;
  WRITELOG("Connecting to server %s:%d", pchserverIP, m_iServerPort);

  //try to connect server 
  connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));

  //check and wait for the socket to be ready with timeout
  fd_set fdWrite;
  FD_ZERO(&fdWrite);
  FD_SET(m_hSocket, &fdWrite);
  int iRet = select(0, NULL, &fdWrite, NULL, &m_stTimeout);

  //decide success or failure
  if((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
  {
    return true;
  }

  return false;
}

重试

的重新启动连接

代码语言:javascript
复制
bool CTCPCommunication::RetryConnection()
{
  bool bSuccess = CloseConnection();
  if (bSuccess == false)
  {
    ReportStatus(App_Err_Retry);
    WRITELOG("Unabled to attempt retry as existing connection could not be closed, error: %d", WSAGetLastError());

    return bSuccess;
  }
  bSuccess = OpenConnection(true);
  return bSuccess;
}

将数据上载到服务器

代码语言:javascript
复制
bool CTCPCommunication::UploadDataPacket(char* pchSendData, int iSendDataLen, MessageID eSendMessageID, CString csSendPacketGUID)
{
  bool bSuccess = false;
  m_iRetryCount = 0;

  while (m_iRetryCount <= MAX_RETRY)
  {
    // Pushing data packet to socket
    bSuccess = SendSocketData(pchSendData, iSendDataLen);

    if (bSuccess == true)
    {
      // Receive data from socket
      char chRecvBuff[MAX_RECV_LEN+1] = {0};
      bSuccess = ReceiveSocketData(chRecvBuff, MAX_RECV_LEN+1);

      // Verify response packet for proper GUID
      if (bSuccess == true)
      {
        CString csRecvBuff = CString(chRecvBuff);
        bSuccess = ValidateACK(eSendMessageID, csRecvBuff, csSendPacketGUID);
        if (bSuccess == true)
        {
          break;
        }
      }
    }

    if (bSuccess == false)
    {
      RetryConnection();
      m_iRetryCount++;
      if(m_iRetryCount <= MAX_RETRY)
      {
        ReportStatus(App_Stat_Retry, m_iRetryCount);
        WRITELOG("Attempting retry %d", m_iRetryCount);
      }
    }
  }

  return bSuccess;
}

发送套接字数据

代码语言:javascript
复制
bool CTCPCommunication::SendSocketData(char* pchData, int iBuffLen)
{
  bool bSuccess = true;
  while (iBuffLen > 0)
  {
    //check whether the socket is ready to write data
    fd_set fdWrite;
    FD_ZERO(&fdWrite);
    FD_SET(m_hSocket, &fdWrite);
    int iRet = select(0, NULL, &fdWrite, NULL, &m_stTimeout);

    if ((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
    {
      int iSentLen = send(m_hSocket, pchData, iBuffLen, 0);

      //sending failed due to socket error
      if (iSentLen == SOCKET_ERROR)
      {
        WRITELOG("Call to socket API 'send' failed, error: %d", WSAGetLastError());
        bSuccess = false;
        break;
      }

      pchData     += iSentLen;
      iBuffLen    -= iSentLen;
    }
    else
    {
      WRITELOG("Call to socket API 'select' failed inside send method, error: %d", WSAGetLastError());
      bSuccess = false;
      break;
    }
  }
  return bSuccess;
}

接收套接字数据

代码语言:javascript
复制
bool CTCPCommunication::ReceiveSocketData(char* pchBuff, int iBuffLen)
{
  bool bSuccess = true;
  while ((iBuffLen-1) > 0)
  {
    //check whether the socket is ready to read data
    fd_set fdRead;
    FD_ZERO(&fdRead);
    FD_SET(m_hSocket, &fdRead);
    int iRet = select(0, &fdRead, NULL, NULL, &m_stTimeout);

    if ((iRet > 0) && (FD_ISSET(m_hSocket, &fdRead)))
    {
      int iRcvdLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);

      //receive failed due to socket error
      if (iRcvdLen <= 0)
      {
        WRITELOG("Call to socket API 'recv' failed, error: %d", WSAGetLastError());
        bSuccess = false;
        break;
      }

      pchBuff  += iRcvdLen;
      iBuffLen -= iRcvdLen;
    }
    else
    {
      WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
      bSuccess = false;
      break;
    }
  }
  return bSuccess;
}
EN

回答 2

Code Review用户

发布于 2015-04-29 20:09:54

首先,不应将值与if语句中的布尔文本进行比较。这最好写成bSuccess,而不是bSuccess == true!bSuccess,而不是bSuccess == false

其次,您正在这里修改一个局部变量,一旦离开该方法,它就会超出范围:

代码语言:javascript
复制
else
{
  WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
  bSuccess = false;
  break;
}

在此之后,您就脱离了循环和return bSuccess;。这可以用来显示您正在立即返回这样的故障信号:

代码语言:javascript
复制
else
{
  WRITELOG("Call to socket API 'select' failed inside recv method, error: %d", WSAGetLastError());
  return false;
}

第三,你有这样的片段:

代码语言:javascript
复制
//decide success or failure
if((iRet > 0) && (FD_ISSET(m_hSocket, &fdWrite)))
{
  return true;
}

return false;

这可以在没有ifs的情况下写成:

代码语言:javascript
复制
return (iRet > 0) && FD_ISSET(m_hSocket, &fdWrite);

第一个语句周围的括号是不必要的,但可能有助于可读性。

票数 5
EN

Code Review用户

发布于 2015-04-29 21:19:56

仅测试套接字是否可写是不够的。反过来说,这只意味着send不会阻塞,而在失败的connect之后,它确实会立即返回( errno设置为ENOTCONN)。测试成功的一种标准方法是使用

代码语言:javascript
复制
    int error;
    int len = sizeof(error);
    getsockopt(n_hSocket, SOL_SOCKET, SO_ERROR, &error, &len);

如果error为0,则连接成功。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/88396

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档