0

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 error connection 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

Patton
  • 125
  • 1
  • 11
  • What happened to the code you got last time? I see `sleep`s, `new`, `delete` etc. That's not good code. – sehe Mar 20 '18 at 03:17
  • Also, it's unclear to me what the problem is: _"Failed: it only works when the duration is short: like 10 secs."_ - what duration? _"If longer than that handle_connect() return error connection timed out)"_ - The code in my last answer [would simply schedule a new connection attempt if `handle_connect()` received an error](https://github.com/sehe/asio/blob/f7be7db129f6e7bf09720096a35d7ecdcd42ee58/example/cpp03/ssl/client.cpp#L95) – sehe Mar 20 '18 at 03:20
  • Hi, I mean user actively call disconnect function (not connection dropped by network like last time). If the user call `disconnect()` then wait more than 10 secs, then call `connect()` the connection is failed with error: `connection timed out`. – Patton Mar 20 '18 at 03:36
  • Your do_reconnect has a rogue "else" block. Can you fix the code to be what you actually use? – sehe Mar 20 '18 at 12:11
  • This is sadly what I actually use :( I just strip the class that compose the message to be written by `SSLHandler` :p – Patton Mar 20 '18 at 15:05
  • If that is true, I can stop looking at your code. Because it will never compile (well... I can do `#define else` of course). Please look at the `do_reconnect` function. – sehe Mar 20 '18 at 15:11

1 Answers1

1

I fixed the sample to be selfcontained, and ran it against a demo server:

#include <iostream>
#include <sstream>
#include <vector>

#ifdef STANDALONE
    namespace {
        struct LogTx {
            std::stringstream _ss;
            std::ostream& _os;
            bool _armed = true;

            LogTx(std::ostream& os) : _os(os) {}
            LogTx(LogTx&& rhs) : _ss(std::move(rhs._ss)), _os(rhs._os) { rhs._armed = false; }
            ~LogTx() { if (_armed) _os << _ss.rdbuf() << std::endl; }

            LogTx operator<<(std::ostream&(&v)(std::ostream&)) { _ss << v; return std::move(*this); }
            template <typename T> LogTx operator<<(T&& v) { _ss << v; return std::move(*this); }
        };

    }

#   define LOG_FATAL LogTx(std::cerr) << "FATAL: "
#   define LOG_TRACE LogTx(std::clog) << "TRACE: "
#   define LOG_ERROR LogTx(std::cerr) << "ERROR: "
#   define LOG_INFO  LogTx(std::clog) << "INFO:  "
#   define Q_UNUSED(a) static_cast<void>(a)

    namespace {
        struct Connector {
            void sendDisconnectedStatus()               { LOG_INFO << "Disconnected"; }
            void setReadMsg(std::vector<char> const& v) { LOG_INFO << "response: '" << std::string(v.begin(), v.end()) << "'"; }
        };
    }
#endif

#ifndef SSLHANDLER_H
#define SSLHANDLER_H
#include <boost/endian/arithmetic.hpp> // for big_uint32_t
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <queue>
#include <string>
#include <thread>

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;
    Connector *mpConnector; // ptr to object compose message
    boost::asio::deadline_timer timer_;
    char reply_[0x1 << 16]; //=65356 bytes

    size_t getFirstFour() {
        return *boost::asio::buffer_cast<boost::endian::big_uint32_t *>(boost::asio::buffer(reply_));
    };

    int mode;
    uint32_t size;
    std::vector<char> requestMsg;
    std::vector<char> replyMsg;
    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);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));

    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) {
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    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();
        LOG_TRACE << "Decoded size: " << size;

        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() {
    //    socket_.shutdown();
    isConnectionOk = false;
    //    return;
    if (isDestructing) {
        LOG_TRACE << "Is destructing ssl connect so abort ";
        return;
    } 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";
        }
    }
}

int main() {
    Connector c;

    boost::asio::io_service svc;
    boost::asio::ssl::context ctx(boost::asio::ssl::context_base::sslv23_client);

    SSLHandler h(svc, ctx, boost::asio::ip::tcp::resolver{svc}.resolve({{}, 6767}), &c);
    h.setRequestMsg({'h','e','l','l','o','\n','w','o','r','l','d'});
    h.connectToServer();

    svc.run();
}

Now, I didn't see any issue, regardless of how long it took for the server to be back after interruption.

You mention

Hi, I mean user actively call disconnect function (not connection dropped by network like last time). If the user call disconnect() then wait more than 10 secs, then call connect() the connection is failed with error: connection timed out. – Patton 11 hours ago

There's no such disconnect() function in your code, nor can I see how it's likely implemented. Therefore, either

  • the problem is on the server-side (which stops accepting connections or completing SSL handshake?)
  • the problem is in the code you don't show
sehe
  • 374,641
  • 47
  • 450
  • 633
  • You can `Ctrl F` this sentence "and disconnect like this" and see what I do in the upper layer, basically I just delete the `SSLHandler` object and create a new one :p – Patton Mar 20 '18 at 15:36
  • ah. I missed that part. I assumed the code was self-contained and I was searching in my editor – sehe Mar 20 '18 at 15:38
  • @Patton where do you run the `io_service`? Could it be it simply runs out of work? – sehe Mar 20 '18 at 15:56
  • `io_service` was run in another object, I pretty sure it was valid and untouched (I put log at the destructor of it, it's always called at the last, before `SSLHandler` and `Connector` got destroyed) – Patton Mar 20 '18 at 16:03
  • That is neither here or there. I didn't wonder whether it was changed. Nor did I wonder whether it was destructed. I wondered whether it runs out of work after some time. (Also, you mean "***after*** `SSLHandler` and `Connector` got destroyed"? Please be careful and as exact as possible) – sehe Mar 20 '18 at 16:04
  • https://stackoverflow.com/questions/15568100/confused-when-boostasioio-service-run-method-blocks-unblocks – sehe Mar 20 '18 at 16:05
  • I have 2 questions: 1.Should I restart `io_service` as well? 2. If you were me would you write unit test with a class with that much dependencies? – Patton Mar 20 '18 at 16:37
  • What do unit tests have to do with it? Whether or not you need to restart the io_service depends (only) on whether it had completed. – sehe Mar 20 '18 at 20:45
  • I asked unit test because I found my implement kinda buggy and have too much dependencies :( BTW, I added the [handler tracking](http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/overview/core/handler_tracking.html), I was so confuse after the disconnect, the first reconnect fired no handler. – Patton Mar 21 '18 at 10:43