In the SSL client server model, I use the following code to read data from a socket on the client or server side.
I only read data when there is data. To find out when data is available, I will check the available() method on lowest_layer() asio::ssl::stream . After sending 380 bytes from the client to the server and entering the reading method on the server, I see the following.
- This is the buffer that I set.
'N is the size of the buffer that I set.
'A1 is the result of available () before reading and reports 458 bytes.
'R is the number of bytes actually read. He will report 380, which is correct.
'A2 is the result of available () after reading and will report 0 bytes. This is what I expect since my client sent 380 bytes and I read all of them.
Why is the first available() call reporting too many bytes?
Types:
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket; typedef boost::shared_ptr<SslSocket> ShpSslSocket;
Users
ShpSslSocket m_shpSecureSocket;
Part of the reading 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 information:
So, I changed my reading method to more carefully check if there is more data available for reading from the boost :: asio :: ssl :: stream. Not only do I need to check if there is data at the socket level, but there may also be data stuck in OpenSSL buffers. SSL_peek does the trick. In addition to checking the available data, it also checks the status of the TCP port and does all this until there is a timeout.
Here is the complete method for reading 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; }