I've created a program that makes use of boost's ssl implementation to send and receive small packets to a remote server using async_read_some()
and async_write_some()
. The read and write are wrapped in a strand to prevent concurrent reading and writing. Additionally the wrapper functions I've created for each contain a mutex to further prevent concurrent access (possibly overkill, but can't hurt). The writes are stored on a write queue where they are sent when the thread is notified that data is available.
The issue I'm experiencing occurs when a large number of writes are executed sequentially, resulting in varying errors such as Second Chance Assertion Failed and Access Violation. I also get a read error of "decryption failed or bad record mac".
From the research I've done so far, I've found that SSL sockets can become corrupted if reads and writes are performed concurrently - at least according to the discussions here and here where the OP's symptoms are very similar to mine. He also states that the strand he was using was not functioning, but I don't understand his solution. This would make sense with the issues I'm having due to the methods I'm attempting to use to prevent concurrent reads and writes.
A workaround I've been using is to throttle the sequential writes so that there's a gap of at least 20ms between each. Any less than this and I start getting errors. This workaround isn't great though, because at some point there may still be a concurrent read/write resulting in errors.
Here's a summary of my read/write code:
void client::write(std::string message, char packetType) {
boost::lock_guard<boost::shared_mutex> lock(writeInProgressMutex);
std::string preparedMessage = prepareWrite(message, packetType);
char data_sent[2048];
for(std::string::size_type i = 0; i < preparedMessage.size(); ++i) {
data_sent[i] = preparedMessage[i];
if (i + 1 == preparedMessage.size()) {
data_sent[i+1] = NULL;
}
}
socket_->async_write_some(boost::asio::buffer(data_sent), strand_.wrap(boost::bind(&client::handle_write, this, boost::asio::placeholders::error)));
}
void client::read() {
boost::lock_guard<boost::shared_mutex> lock(readInProgressMutex);
socket_->async_read_some(boost::asio::buffer(data_), strand_.wrap(boost::bind(&client::handle_read, this, boost::asio::placeholders::error)));
}
I've experimented with different types of mutexes, so I don't think that is the issue. If anyone knows a way to ensure my strand is doing its job or you can see some obvious errors in my code/design please let me know!