Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost asio tcp socket available reports incorrect number of bytes

In SSL client server model, I use the code below to read data from the socket on either client or server side.

I only read data when there is data available. To know when there is data available, I check the available() method on the lowest_layer() of the asio::ssl::stream. After I send 380 bytes from the client to the server and enter the read method on the server, I see the following.

‘s’ is the buffer I supplied.
‘n’ is the size of the buffer I supplied.
‘a1’ is the result of available() before the read and will report 458 bytes.
‘r’ is the number of bytes actually read. It will report 380, which is correct.
‘a2’ is the result of available() after the read and will report 0 bytes. This is what I expect, since my client sent 380 bytes and I have read them all.

Why does the first call to available() report too many bytes?

Types:

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

Members:

ShpSslSocket                     m_shpSecureSocket; 

Part of the read method:

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();

Added info:

So I changed my read method to more thoroughly check if there is still data available to be read from the boost::asio::ssl::stream. Not only do I need to check if there is data available on the socket level, but there may also be data stuck in the OpenSSL buffers somewhere. SSL_peek does the trick. Next to checking for available data, it also checks the TCP port status and does all this as long as there is no timeout.

Here is the complete read method of the boost::iostreams::device class that I created.

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;
}
like image 401
Radboud Platvoet Avatar asked Oct 18 '12 07:10

Radboud Platvoet


1 Answers

So maybe I know why this is. It is an SSL connection and therfor the transfered bytes will be encrypted. Encrypted data may well be of a different size because of the block size. I guess that answers the question why the number of bytes available on TCP level is different than the number of bytes that comes out of a read.

like image 79
Radboud Platvoet Avatar answered Oct 30 '22 13:10

Radboud Platvoet