Boost asio tcp socket可用报告错误的字节数

时间:2012-10-18 07:25:42

标签: c++ boost boost-asio

在SSL客户端服务器模型中,我使用下面的代码从客户端或服务器端的套接字读取数据。

我只在有可用数据时读取数据。要知道何时有可用数据,请检查available() lowest_layer()上的asio::ssl::stream方法。 在我从客户端向服务器发送380个字节并在服务器上输入read方法后,我看到以下内容。

's'是我提供的缓冲区 'n'是我提供的缓冲区的大小。
'a1'是读取前的available()的结果,将报告458个字节 'r'是实际读取的字节数。它将报告380,这是正确的 'a2'是读取后的available()的结果,将报告0字节。这是我的期望,因为我的客户端发送了380个字节,我已经全部阅读了。

为什么第一次调用available()会报告太多字节?

类型:

/**
 * Type used as SSL Socket. Handles SSL and socket functionality.
 */
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket;
/**
 * A shared pointer version of the SSL Socket type.
 */
typedef boost::shared_ptr<SslSocket>                           ShpSslSocket;

成员:

ShpSslSocket                     m_shpSecureSocket; 

读取方法的一部分:

std::size_t a1 = 0;
if ((a1 = m_shpSecureSocket->lowest_layer().available()) > 0)
{
   r += boost::asio::read(*m_shpSecureSocket,
                          boost::asio::buffer(s, n),
                          boost::asio::transfer_at_least(1));
}

std::size_t a2 = m_shpSecureSocket->lowest_layer().available();

添加信息:

所以我更改了我的read方法,以便更彻底地检查是否仍然可以从boost :: asio :: ssl :: stream中读取数据。我不仅需要检查套接字级别上是否有可用的数据,而且某些地方的OpenSSL缓冲区中可能还存在数据。 SSL_peek可以解决问题。在检查可用数据之后,它还检查TCP端口状态,并且只要没有超时就完成所有这些。

这是我创建的boost :: iostreams :: device类的完整读取方法。

std::streamsize SslClientSocketDevice::read(char* s, std::streamsize n)
{
   // Request from the stream/device to receive/read bytes.
   std::streamsize r = 0;

   LIB_PROCESS::TcpState eActualState = LIB_PROCESS::TCP_NOT_EXIST;

   char chSslPeekBuf; // 1 byte peek buffer

   // Check that there is data available. If not, wait for it.
   // Check is on the lowest layer (tcp). In that layer the data is encrypted.
   //  The number of encrypted bytes is most often different than the number
   //  of unencrypted bytes that would be read from the secure socket.
   // Also: Data may be read by OpenSSL from the socket and remain in an
   //  OpenSSL buffer somewhere. We also check that.
   boost::posix_time::ptime start = BOOST_UTC_NOW;
   int         nSslPeek  = 0;
   std::size_t nAvailTcp = 0;
   while ((*m_shpConnected) &&
          (LIB_PROCESS::IpMonitor::CheckPortStatusEquals(GetLocalEndPoint(),
                                                         GetRemoteEndPoint(),
                                                         ms_ciAllowedStates,
                                                         eActualState)) &&
          ((nAvailTcp = m_shpSecureSocket->lowest_layer().available()) == 0) &&
          ((nSslPeek  = SSL_peek(m_shpSecureSocket->native_handle(), &chSslPeekBuf, 1)) <= 0) && // May return error (<0) as well
          ((start + m_oReadTimeout) > BOOST_UTC_NOW))
   {
      boost::this_thread::sleep(boost::posix_time::millisec(10));
   }

   // Always read data when there is data available, even if the state is no longer valid.
   // Data may be reported by the TCP socket (num encrypted bytes) or have already been read
   //  by SSL and not yet returned to us.
   // Remote party can have sent data and have closed the socket immediately.
   if ((nAvailTcp > 0) || (nSslPeek > 0))
   {
      r += boost::asio::read(*m_shpSecureSocket,
                             boost::asio::buffer(s, n),
                             boost::asio::transfer_at_least(1));
   }

   // Close socket when state is not valid.
   if ((eActualState & ms_ciAllowedStates) == 0x00)
   {
      LOG4CXX_INFO(LOG4CXX_LOGGER, "TCP socket not/no longer connected. State is: " <<
                                    LIB_PROCESS::IpMonitor::TcpStateToString(eActualState));
      LOG4CXX_INFO(LOG4CXX_LOGGER, "Disconnecting socket.");
      Disconnect();
   }

   if (! (*m_shpConnected))
   {
      if (r == 0)
      {
         r = -1; // Signal stream is closed if no data was retrieved.
         ThrowExceptionStreamFFL("TCP socket not/no longer connected.");
      }
   }

   return r;
}

1 个答案:

答案 0 :(得分:1)

所以也许我知道为什么会这样。它是一个SSL连接,并且传输的字节将被加密。由于块大小,加密数据可能具有不同的大小。我想这就回答了为什么TCP级别上可用的字节数与读取时出现的字节数不同的问题。