在Boost.Asio中同时使用SSL套接字和非SSL套接字?

DSB*_*DSB 24 c++ boost-asio

我正在将库转换为Boost.Asio(到目前为止工作得非常好),但我在设计决策方面遇到了一些绊脚石.

Boost.Asio提供对SSL的支持,但boost::asio::ssl::stream<boost::asio::ip::tcp::socket>必须为套接字使用类型.我的库可以选择连接到SSL服务器或正常连接,所以我创建了一个带有两个套接字的类,如下所示:

class client : public boost::enable_shared_from_this<client>
{
public:
    client(boost::asio::io_service & io_service, boost::asio::ssl::context & context) : socket_(io_service), secureSocket_(io_service, context) {}
private:
    boost::asio::ip::tcp::socket socket_;
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
};
Run Code Online (Sandbox Code Playgroud)

并且有一堆参考的处理程序socket_.(例如,我有socket_.is_open()几个地方,需要成为secureSocket_.lowest_layer().is_open()另一个插座.)

任何人都可以建议最好的方法来解决这个问题吗?我宁愿不为此目的创建一个单独的类,因为这意味着要复制很多代码.

编辑:我改写了我原来的问题,因为我误解了OpenSSL函数的用途.

Nic*_*ole 18

我回答这个问题的时间比较晚,但我希望这会对其他人有所帮助.Sam的回答包含了一个想法的细节,但在我看来并没有放弃.

这个想法来自于asio在流中包装SSL套接字的观察结果.所有这个解决方案都是类似地包装非SSL套接字.

在SSL和非SSL套接字之间具有统一外部接口的期望结果是使用三个类完成的.一,基础,有效地定义了接口:

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));
    }
};
Run Code Online (Sandbox Code Playgroud)

两个子类包装SSL和非SSL套接字.

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();
    }
};
Run Code Online (Sandbox Code Playgroud)

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();
    }
};
Run Code Online (Sandbox Code Playgroud)

每次调用asio函数时都使用getSocketForAsio(),而不是传递对Socket对象的引用.例如:

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));
Run Code Online (Sandbox Code Playgroud)

请注意,Socket存储为指针.我想不出怎样才能隐藏多态.

惩罚(我认为不太好)是用于获取非SSL套接字的额外间接级别.

  • 这些都不会正常工作.首先是因为你不能对ssl :: stream的底层ip :: tcp :: socket进行原始读写.其次,它不适用于无堆栈或堆栈协程,因为您不支持通过async_result机制自定义返回值. (2认同)

Sam*_*ler 14

有几种方法可以做到这一点.在过去,我做过类似的事情

if ( sslEnabled )
    boost::asio::async_write( secureSocket_ );
} else {
    boost::asio::async_write( secureSocket_.lowest_layer() );
}
Run Code Online (Sandbox Code Playgroud)

通过大量的if/else陈述,这很快就会变得混乱.您还可以创建一个抽象类(伪代码 - 过度简化)

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

然后创建一个派生类SecureSocket来操作secureSocket_而不是socket_.我不认为它会被重复了很多代码,它可能比清洁if/else每当你需要async_readasync_write.


jtb*_*tbr 5

当然,问题是 tcp::socket 和 ssl“socket”不共享任何共同的祖先。但是,打开套接字后使用套接字的大多数函数都共享完全相同的语法。因此,最干净的解决方案是使用模板。

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

此函数可与普通 tcp::sockets 以及安全 SSL 套接字一起使用:

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 ...
Run Code Online (Sandbox Code Playgroud)