C ++ tcp套接字连接重试方法

时间:2015-04-28 09:46:26

标签: c++ sockets tcp

在开发可以交换某些数据的示例客户端服务器应用程序之后,我正在尝试在其中实现重试机制。目前我的申请是遵循以下协议:

  1. 客户端连接到服务器(非阻塞模式),超时3秒,恢复2次。
  2. 开始从客户端发送固定长度的数据。发送有一些错误检查它是否正在发送完整的数据。
  3. 从服务器接收响应(超时:3秒)并验证。如果收到错误响应,请重新发送数据并等待响应。如果失败,重复两次。
  4. 对于上面的实现代码部分看起来像下面这样:

    1. connect()和select()用于打开连接
    2. select()和send()用于数据发送
    3. select()和recv()用于接收数据
    4. 现在我正在根据套接字函数的返回类型进行重试,如果send()或recv()失败,我正在重试相同的方法。但不记得connect()。

      我通过在数据传输之间重新启动服务器测试了这个东西,结果客户端无法与服务器通信并且在几次重试后退出,我相信这是因为没有connect()调用重试方法。

      有什么建议吗?

      接收套接字数据的示例代码

      bool CTCPCommunication::ReceiveSocketData(char* pchBuff, int iBuffLen)
      {
        bool bReturn = 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);
      
        //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)))
        {
          DWORD dwStartTime = GetTickCount();
          DWORD dwCurrentTime = 0;
      
          while ((iBuffLen-1) > 0)
          {
            int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
            dwCurrentTime = GetTickCount();
      
            //receive failed due to socket error
            if (iRcvLen == SOCKET_ERROR)
            {
              if((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000)
              {
                WRITELOG("Call to socket API 'recv' failed after 3 secs continuous retries, error: %d", WSAGetLastError());
                bReturn = false;
                break;
              }
            }
            //connection closed by remote host
            else if (iRcvLen == 0)
            {
              WRITELOG("recv() returned zero - time to do something: %d", WSAGetLastError());
              break;
            }
      
            pchBuff  += iRcvLen;
            iBuffLen -= iRcvLen;
          }
        }
        else
        {
          WRITELOG("Call to API 'select' failed inside 'ReceiveSocketData', error: %d", WSAGetLastError());
          bReturn = false;
        }
      
        return bReturn;
      }
      

1 个答案:

答案 0 :(得分:1)

  

目前我的申请符合以下协议:

     
      
  1. 客户端连接到服务器(非阻塞模式),超时3秒,重试次数为2次。
  2.   

您无法重试连接。您必须关闭连接尝试失败的套接字,创建一个新套接字,然后再次调用connect()

  
      
  1. 开始从客户端发送固定长度的数据。发送有一些错误,检查是否正在发送完整数据。
  2.   

在阻止模式下这不是必需的:POSIX标准保证阻塞模式send()将发送所有数据,或者因错误而失败。

  
      
  1. 从服务器接收响应(超时:3秒)并验证。如果收到错误响应,请重新发送数据并等待响应。如果失败,重复两次。
  2.   

这是一个坏主意。很可能所有数据都将到达,包括所有重试,或者没有。如果您使用此技术,则需要确保您的交易是 idempotent 。您还需要密切关注实际的超时时间。一般来说3秒是不够的。起点是预期服务时间的两倍。

  

对于上面的实现代码部分看起来像下面这样:

   connect() and select() for opening connection
   select() and send() for data send
   select() and recv() for data receiving

在阻止模式下,您不需要select()。您可以使用SO_RCVTIMEO.

设置读取超时
  

现在我根据套接字函数的返回类型进行重试,如果send()或recv()失败,我会重试相同的方法。但不记得connect()。

     

我通过在数据传输之间重新启动服务器测试了这个东西,结果客户端无法与服务器通信并且在几次重试后退出,我相信这是因为没有connect()调用重试方法。

如果这是真的,你会得到一个错误。