Patton
Patton

Reputation: 125

Get error boost asio connection timed out when try to reconnect to server

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:

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

Upvotes: 0

Views: 1966

Answers (1)

sehe
sehe

Reputation: 393769

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

Upvotes: 1

Related Questions