I need to write a class that handle ssl connection (read/write char array) to a black box server. I need to implement disconnect/connect function. But it ain't work as expected.
Use case:
- User connect to server (worked: can send and receive messages)
- User disconnect to server, wait for a while then reconnect again. (Failed: it only works when the duration is short: like 10 secs. If longer than that
handle_connect()
return errorconnection timed out
)
Here is the source code to the class and how I use it:
boost::asio::io_service &mioService;
SSLHandler* mpSSLConnection;
void Connector::setupConnection()
{
try{
std::string port = std::to_string(mPort);
boost::asio::ip::tcp::resolver resolver(mioService);
boost::asio::ip::tcp::resolver::query query(mHost, port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ssl::context context(boost::asio::ssl::context::sslv23);
// context.load_verify_file("key.pem");
if(mpSSLConnection == nullptr)
mpSSLConnection = new SSLHandler(mioService,context,iterator,this);
}catch (std::exception& e){
std::cerr << "Exception: " << e.what() << "\n";
}
// actually this line is called from outside the func
mpSSLConnection->connectToServer();
}
and disconnect like this
void Connector::disconnect()
{
isSetDisconnectedToSrv = true;
mpSSLConnection->setIsDestructing(true);
QThread::msleep(500);
delete mpSSLConnection;
mpSSLConnection = nullptr;
// setupConnection();
isConnectedToServer =false; // we did delete the object handle the ssl connection so...
mpHandler->onServerDisconnected(); // just report to upper layer
}
Finally, the class source code:
#ifndef SSLHANDLER_H
#define SSLHANDLER_H
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <queue>
#include <boost/lockfree/spsc_queue.hpp>
class Connector;
const int READ_SIZE =0;
const int READ_MSG=1;
class SSLHandler
{
public:
SSLHandler(boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator, Connector* pConnector)
: socket_(io_service, context) , mEndpointIterator (endpoint_iterator) , mpConnector (pConnector),
timer_{ io_service},
isConnectionOk {false}
{
LOG_TRACE << "creating new sslhandler";
socket_.set_verify_mode(boost::asio::ssl::context::verify_none);
socket_.set_verify_callback(boost::bind(&SSLHandler::verify_certificate, this, _1, _2));
mode = READ_SIZE;
}
~SSLHandler();
bool verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx);
void handle_connect(const boost::system::error_code& error);
void handle_handshake(const boost::system::error_code& error);
void handle_write(const boost::system::error_code& error, size_t bytes_transferred);
void handle_write_auth(const boost::system::error_code& error, size_t bytes_transferred);
void handle_read_msgsize(const boost::system::error_code& error, size_t bytes_transferred);
void handle_read_message(const boost::system::error_code& error, size_t bytes_transferred);
void connectToServer();
void do_reconnect();
void handle_reconnect_timer(boost::system::error_code ec);
void writeMessage(std::vector<char> &array);
void setRequestMsg(std::vector<char> &&array);
void setIsDestructing(bool value);
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::ip::tcp::resolver::iterator mEndpointIterator;
boost::asio::deadline_timer timer_;
char reply_[0x1 << 16]; //=65356 bytes
int mode;
uint32_t size;
std::vector<char> requestMsg;
std::vector<char> replyMsg;
Connector* mpConnector; // ptr to object compose message
std::queue<std::vector < char> > mQueueMsg;
bool isConnectionOk;
bool isDestructing =false;
private:
void writeMessageWithQueue(std::vector<char> &array);
};
#endif // SSLHANDLER_H
#include "sslhandler.h"
#include "connector.h"
#include "BoostLogger.h"
#include <QThread>
#include "boost/enable_shared_from_this.hpp"
SSLHandler::~SSLHandler()
{
LOG_FATAL << "ssl handler shutdown";
if(isConnectionOk){
socket_.lowest_layer().close();
boost::system::error_code ec;
socket_.shutdown(ec);
if(ec){
LOG_FATAL << "ssl handler socket shutdown with err: " << ec.message();
}
LOG_TRACE << "ssl handler shutdown complete";
}
}
bool SSLHandler::verify_certificate(bool preverified, boost::asio::ssl::verify_context &ctx)
{
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
std::cout << "Verifying:\n" << subject_name << std::endl;
return preverified;
}
void SSLHandler::handle_connect(const boost::system::error_code &error)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
LOG_TRACE << "get past destructing";
if(!error){
isConnectionOk = true;
LOG_TRACE << "Connection OK!" << std::endl;
socket_.async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&SSLHandler::handle_handshake, this, boost::asio::placeholders::error));
}else{
LOG_FATAL << "Connect failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_handshake(const boost::system::error_code &error)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if(!error){
std::cout << "Sending request: " << std::endl;
boost::asio::async_write(socket_,
boost::asio::buffer(requestMsg.data(), requestMsg.size()),
boost::bind(&SSLHandler::handle_write_auth, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Handshake failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_write(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (error) {
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
Q_UNUSED(bytes_transferred);
usleep(1e4);
if (!mQueueMsg.empty()) {
mQueueMsg.pop();
if(!mQueueMsg.empty()){
auto msg = mQueueMsg.front();
writeMessageWithQueue(msg);
}
}
else{
LOG_ERROR << "Empty queue messages!";
}
}
void SSLHandler::handle_write_auth(const boost::system::error_code &error, size_t bytes_transferred)
{
usleep(1e5);
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
if(mode==READ_SIZE){
mode = READ_MSG;
std::cerr << "\nSending request read size OK!\n" << std::endl;
// char respond[bytes_transferred] = "";
boost::asio::async_read(socket_, boost::asio::buffer(reply_,4),
boost::bind(&SSLHandler::handle_read_msgsize,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// std::cerr << "respond is " ;
}
}else{
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_msgsize(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
//first 4 bytes contain size of message
size = getFirstFour();
mode = READ_SIZE;
boost::asio::async_read(socket_, boost::asio::buffer(reply_,size),
boost::bind(&SSLHandler::handle_read_message,
this,
// mWriteId++,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_message(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
replyMsg.clear();
replyMsg.assign(reply_,reply_+ size);
mpConnector->setReadMsg(replyMsg);
mode = READ_SIZE;
// read next message size
boost::asio::async_read(socket_, boost::asio::buffer(reply_,4),
boost::bind(&SSLHandler::handle_read_msgsize,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::connectToServer()
{
// boost::asio::ip::tcp::resolver r(socket_.get_io_service());
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
boost::asio::async_connect(socket_.lowest_layer(), mEndpointIterator, boost::bind(&SSLHandler::handle_connect, this, boost::asio::placeholders::error));
LOG_TRACE << "async_connect called";
}
void SSLHandler::do_reconnect()
{
// return;
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
// socket_.shutdown();
isConnectionOk = false;
else{
socket_.lowest_layer().cancel();
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&SSLHandler::handle_reconnect_timer, this, boost::asio::placeholders::error()));
}
}
void SSLHandler::handle_reconnect_timer(boost::system::error_code ec)
{
if(!ec){
connectToServer();
}
else{
LOG_TRACE << "Error with reconnect timer : " << ec.message();
}
}
void SSLHandler::writeMessageWithQueue(std::vector<char> &array)
{
// std::cerr << "write : " << (void*) array.data() << " | " << array.size() << std::endl;
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
boost::asio::async_write(socket_,
boost::asio::buffer(array.data(), array.size()),
boost::bind(&SSLHandler::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void SSLHandler::writeMessage(std::vector<char> &array)
{
if(mQueueMsg.size()==0){
mQueueMsg.push(array);
writeMessageWithQueue(array);
}
else
mQueueMsg.push(array);
}
void SSLHandler::setRequestMsg(std::vector<char> &&array)
{
requestMsg = std::move(array);
}
void SSLHandler::setIsDestructing(bool value)
{
LOG_INFO << "ssl connection destructing set as " << value;
isDestructing = value;
if(isDestructing == true){
if(isConnectionOk){
socket_.lowest_layer().cancel();
// socket_.shutdown();
LOG_INFO << "ssl connection destructing get pass shutdown";
}
}
}
PS: Here is the handler tracking which is weird, after (call) disconnect()
and reconnect again: no hander was fired, so no handshake after connection is established :(
@asio|1521627714.863517|0|resolver@0x7fbe2affc898.cancel Empty fz params!Empty fz rules!Empty fz param!@asio____________ socket_: 0x7fbe10005150 @asio____________192.168.2.36 @asio|1521627714.864161|0*1|socket@0x7fbe10005150.async_connect @asio: connect called @asio|1521627714.865136|>1|ec=system:0 @asio|1521627714.865375|1*2|socket@0x7fbe10005150.async_send @asio|1521627714.865416|<1| @asio|1521627714.865421|>2|ec=system:0,bytes_transferred=517 @asio|1521627714.865429|2*3|socket@0x7fbe10005150.async_receive @asio|1521627714.865451|<2| @asio|1521627714.866829|>3|ec=system:0,bytes_transferred=994 @asio|1521627714.867764|3*4|socket@0x7fbe10005150.async_send @asio|1521627714.867792|<3| @asio|1521627714.867798|>4|ec=system:0,bytes_transferred=326 @asio|1521627714.867809|4*5|socket@0x7fbe10005150.async_receive @asio|1521627714.867817|<4| @asio|1521627714.870094|>5|ec=system:0,bytes_transferred=234 @asio|1521627714.870271|5*6|socket@0x7fbe10005150.async_send @asio|1521627714.870318|<5| @asio|1521627714.870333|>6|ec=system:0,bytes_transferred=154 @asio|1521627714.970430|6*7|socket@0x7fbe10005150.async_receive @asio|1521627714.970443|<6| @asio|1521627714.970449|>7|ec=system:0,bytes_transferred=138 @asio|1521627714.970470|7*8|socket@0x7fbe10005150.async_receive @asio|1521627714.970475|<7| @asio|1521627714.970479|>8|ec=system:0,bytes_transferred=0 @asio|1521627714.971418|8*9|socket@0x7fbe10005150.async_send @asio|1521627714.971771|8*10|deadline_timer@0x5290628.async_wait @asio|1521627714.972004|8*11|socket@0x7fbe10005150.async_receive @asio|1521627714.972012|<8| @asio|1521627714.972017|>9|ec=system:0,bytes_transferred=138 @asio|1521627714.982098|9*12|socket@0x7fbe10005150.async_send @asio|1521627714.982115|<9| @asio|1521627714.982121|>12|ec=system:0,bytes_transferred=138 @asio|1521627714.992214|12*13|socket@0x7fbe10005150.async_send @asio|1521627714.992244|<12| @asio|1521627714.992255|>11|ec=system:0,bytes_transferred=292 @asio|1521627714.992278|11*14|socket@0x7fbe10005150.async_receive @asio|1521627714.992284|<11| @asio|1521627714.992290|>13|ec=system:0,bytes_transferred=186 @asio|1521627715.002355|<13| @asio|1521627715.002363|>14|ec=system:0,bytes_transferred=0 @asio|1521627715.002469|14*15|socket@0x7fbe10005150.async_receive @asio|1521627715.002479|<14| @asio|1521627715.002487|>15|ec=system:0,bytes_transferred=0 @asio|1521627715.002495|15*16|socket@0x7fbe10005150.async_receive @asio|1521627715.002500|<15| @asio|1521627715.002505|>16|ec=system:0,bytes_transferred=0 @asio|1521627715.002550|16*17|socket@0x7fbe10005150.async_receive @asio|1521627715.002561|<16| @asio|1521627715.002566|>17|ec=system:0,bytes_transferred=154 @asio|1521627715.002581|17*18|socket@0x7fbe10005150.async_receive @asio|1521627715.002586|<17| @asio|1521627715.002590|>18|ec=system:0,bytes_transferred=0 @asio|1521627715.002636|18*19|socket@0x7fbe10005150.async_receive @asio|1521627715.002653|<18| @asio|1521627721.861983|>10|ec=system:0 @asio|1521627721.862105|10*20|socket@0x7fbe10005150.async_send @asio|1521627721.862139|10|deadline_timer@0x5290628.cancel @asio|1521627721.862144|10*21|deadline_timer@0x5290628.async_wait @asio|1521627721.862258|<10| @asio|1521627721.862268|>20|ec=system:0,bytes_transferred=138 @asio|1521627721.872365|<20| @asio|1521627721.872398|>19|ec=system:0,bytes_transferred=138 @asio|1521627721.872436|19*22|socket@0x7fbe10005150.async_receive @asio|1521627721.872443|<19| @asio|1521627721.872447|>22|ec=system:0,bytes_transferred=0 @asio|1521627721.872503|22*23|socket@0x7fbe10005150.async_receive @asio|1521627721.872515|<22| @asio|1521627724.861966|>21|ec=system:0 @asio|1521627724.862091|21*24|socket@0x7fbe10005150.async_send @asio|1521627724.862148|21|deadline_timer@0x5290628.cancel @asio|1521627724.862157|21*25|deadline_timer@0x5290628.async_wait @asio|1521627724.862272|<21| @asio|1521627724.862286|>24|ec=system:0,bytes_transferred=138 @asio|1521627724.872375|<24| @asio|1521627724.872409|>23|ec=system:0,bytes_transferred=138 @asio|1521627724.872457|23*26|socket@0x7fbe10005150.async_receive @asio|1521627724.872465|<23| @asio|1521627724.872469|>26|ec=system:0,bytes_transferred=0 @asio|1521627724.872510|26*27|socket@0x7fbe10005150.async_receive @asio|1521627724.872516|<26| @asio|1521627727.861968|>25|ec=system:0 @asio|1521627727.862084|25*28|socket@0x7fbe10005150.async_send @asio|1521627727.862120|25|deadline_timer@0x5290628.cancel @asio|1521627727.862125|25*29|deadline_timer@0x5290628.async_wait @asio|1521627727.862204|<25| @asio|1521627727.862211|>28|ec=system:0,bytes_transferred=138 @asio|1521627727.872283|<28| @asio|1521627727.872314|>27|ec=system:0,bytes_transferred=138 @asio|1521627727.872362|27*30|socket@0x7fbe10005150.async_receive @asio|1521627727.872366|<27| @asio|1521627727.872371|>30|ec=system:0,bytes_transferred=0 @asio|1521627727.872412|30*31|socket@0x7fbe10005150.async_receive @asio|1521627727.872418|<30| @asio|1521627730.861967|>29|ec=system:0 @asio|1521627730.862072|29*32|socket@0x7fbe10005150.async_send @asio|1521627730.862118|29|deadline_timer@0x5290628.cancel @asio|1521627730.862125|29*33|deadline_timer@0x5290628.async_wait @asio|1521627730.862217|<29| @asio|1521627730.862227|>32|ec=system:0,bytes_transferred=138 @asio|1521627730.872315|<32| @asio|1521627730.872360|>31|ec=system:0,bytes_transferred=138 @asio|1521627730.872406|31*34|socket@0x7fbe10005150.async_receive @asio|1521627730.872412|<31| @asio|1521627730.872416|>34|ec=system:0,bytes_transferred=0 @asio|1521627730.872458|34*35|socket@0x7fbe10005150.async_receive @asio|1521627730.872465|<34| @asio|1521627733.862001|>33|ec=system:0 @asio|1521627733.862114|33*36|socket@0x7fbe10005150.async_send @asio|1521627733.862153|33|deadline_timer@0x5290628.cancel @asio|1521627733.862158|33*37|deadline_timer@0x5290628.async_wait @asio|1521627733.862244|<33| @asio|1521627733.862250|>36|ec=system:0,bytes_transferred=138 @asio|1521627733.872342|<36| @asio|1521627733.872379|>35|ec=system:0,bytes_transferred=138 @asio|1521627733.872416|35*38|socket@0x7fbe10005150.async_receive @asio|1521627733.872422|<35| @asio|1521627733.872424|>38|ec=system:0,bytes_transferred=0 @asio|1521627733.872461|38*39|socket@0x7fbe10005150.async_receive @asio|1521627733.872466|<38| @asio|1521627736.861976|>37|ec=system:0 @asio|1521627736.862158|37*40|socket@0x7fbe10005150.async_send @asio|1521627736.862235|37|deadline_timer@0x5290628.cancel @asio|1521627736.862242|37*41|deadline_timer@0x5290628.async_wait @asio|1521627736.862406|<37| @asio|1521627736.862414|>40|ec=system:0,bytes_transferred=138 @asio|1521627736.872497|<40| @asio|1521627736.872555|>39|ec=system:0,bytes_transferred=138 @asio|1521627736.872622|39*42|socket@0x7fbe10005150.async_receive @asio|1521627736.872638|<39| @asio|1521627736.872641|>42|ec=system:0,bytes_transferred=0 @asio|1521627736.872720|42*43|socket@0x7fbe10005150.async_receive @asio|1521627736.872726|<42| @asio|1521627739.861978|>41|ec=system:0 @asio|1521627739.862096|41*44|socket@0x7fbe10005150.async_send @asio|1521627739.862144|41|deadline_timer@0x5290628.cancel @asio|1521627739.862148|41*45|deadline_timer@0x5290628.async_wait @asio|1521627739.862243|<41| @asio|1521627739.862249|>44|ec=system:0,bytes_transferred=138 @asio|1521627739.872335|<44| @asio|1521627739.872375|>43|ec=system:0,bytes_transferred=138 @asio|1521627739.872421|43*46|socket@0x7fbe10005150.async_receive @asio|1521627739.872425|<43| @asio|1521627739.872429|>46|ec=system:0,bytes_transferred=0 @asio|1521627739.872477|46*47|socket@0x7fbe10005150.async_receive @asio|1521627739.872492|<46| @asio|1521627742.861953|>45|ec=system:0 @asio|1521627742.862121|45*48|socket@0x7fbe10005150.async_send @asio|1521627742.862204|45|deadline_timer@0x5290628.cancel @asio|1521627742.862211|45*49|deadline_timer@0x5290628.async_wait @asio|1521627742.862392|<45| @asio|1521627742.862406|>48|ec=system:0,bytes_transferred=138 @asio|1521627742.872491|<48| @asio|1521627742.872543|>47|ec=system:0,bytes_transferred=138 @asio|1521627742.872592|47*50|socket@0x7fbe10005150.async_receive @asio|1521627742.872600|<47| @asio|1521627742.872605|>50|ec=system:0,bytes_transferred=0 @asio|1521627742.872675|50*51|socket@0x7fbe10005150.async_receive @asio|1521627742.872688|<50| @asio|1521627745.316714|0|socket@0x7fbe10005150.close @asio|1521627745.316777|>51|ec=system:125,bytes_transferred=0 @asio|1521627745.316858|<51| @asio: ~SSLHandler @asio|1521627745.817594|0|resolver@0x7fbe00ff8758.cancel @asio|1521627745.861965|>49|ec=system:0 @asio|1521627745.861984|<49| @asio|1521627749.757091|0|resolver@0x7fbe00ff8648.cancel @asio____________ socket_: 0x7fbde4008890 @asio____________192.168.2.36 @asio|1521627749.757178|0*52|socket@0x7fbde4008890.async_connect @asio: connect called