Using SSL sockets and non-SSL sockets simultaneously in Boost.Asio?

﹥>﹥吖頭↗ 提交于 2019-11-29 22:21:19

There's a couple of ways you can do this. In the past, I've done something like

if ( sslEnabled )
    boost::asio::async_write( secureSocket_ );
} else {
    boost::asio::async_write( secureSocket_.lowest_layer() );
}

Which can get messy pretty quickly with a lot of if/else statements. You could also create an abstract class (pseudo code - oversimplified)

class Socket
{
    public:
       virtual void connect( ... );
       virtual void accept( ... );
       virtual void async_write( ... );
       virtual void async_read( ... );
    private:
        boost::asio::ip::tcp::socket socket_;
};

Then create a derived class SecureSocket to operate on a secureSocket_ instead of socket_. I don't think it would be duplicating a lot of code, and it's probably cleaner than if/else whenever you need to async_read or async_write.

I'm rather late in answering this question, but I hope this will help others. Sam's answer contains the germ of an idea, but doesn't quit go far enough in my opinion.

The idea came about from the observation that asio wraps an SSL socket in a stream. All this solution does is that it wraps the non-SSL socket similarly.

The desired result of having a uniform external interface between SSL and non-SSL sockets is done with three classes. One, the base, effectively defines the interface:

class Socket {
public:
    virtual boost::asio::ip::tcp::socket &getSocketForAsio() = 0;

    static Socket* create(boost::asio::io_service& iIoService, boost::asio::ssl::context *ipSslContext) {
        // Obviously this has to be in a separate source file since it makes reference to subclasses
        if (ipSslContext == nullptr) {
            return new NonSslSocket(iIoService);
        }
       return new SslSocket(iIoService, *ipSslContext);
    }

    size_t _read(void *ipData, size_t iLength) {
        return boost::asio::read(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
    }
    size_t _write(const void *ipData, size_t iLength) {
        return boost::asio::write(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
    }
};

Two sub-classes wrap SSL and non-SSL sockets.

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket_t;
class SslSocket: public Socket, private SslSocket_t {
public:
    SslSocket(boost::asio::io_service& iIoService, boost::asio::ssl::context &iSslContext) :
        SslSocket_t(iIoService, iSslContext) {
    }

private:
    boost::asio::ip::tcp::socket &getSocketForAsio() {
        return next_layer();
    }
};

and

class NonSslSocket: public Socket, private Socket_t {
public:
    NonSslSocket(boost::asio::io_service& iIoService) :
            Socket_t(iIoService) {
    }

private:
    boost::asio::ip::tcp::socket &getSocketForAsio() {
        return next_layer();
    }
};

Every time you call an asio function use getSocketForAsio(), rather than pass a reference to the Socket object. For example:

boost::asio::async_read(pSocket->getSocketForAsio(),
            boost::asio::buffer(&buffer, sizeof(buffer)),
            boost::bind(&Connection::handleRead,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

Notice that the Socket is stored as pointer. I cannot think how else the polymorphism can be hidden.

The penalty (which I don't think great) is the extra level of indirection used to obtain non-SSL sockets.

The problem of course is that tcp::socket and the ssl "socket" don't share the any common ancestor. But most functions for using the socket once it's open share the exact same syntax. The cleanest solution is thus with templates.

template <typename SocketType>
void doStuffWithOpenSocket(SocketType socket) {
   boost::asio::write(socket, ...);
   boost::asio::read(socket, ...);
   boost::asio::read_until(socket, ...);
   // etc...
}

This function will work work with normal tcp::sockets and also secure SSL sockets:

boost::asio::ip::tcp::socket socket_;
// socket_ opened normally ...
doStuffWithOpenSocket<boost::asio::ip::tcp::socket>(socket_); // works!

boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
// secureSocket_ opened normally (including handshake) ...
doStuffWithOpenSocket(secureSocket_); // also works, with (different) implicit instantiation!
// shutdown the ssl socket when done ...

It would compile with something like this:

typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> Socket_t;

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!