Использование сокетов SSL и сокетов без SSL одновременно в Boost.Asio? - PullRequest
23 голосов
/ 18 января 2011

Я нахожусь в процессе преобразования библиотеки в 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_;
};

И внутри есть несколько обработчиков, которые ссылаются на socket_.(Например, у меня есть socket_.is_open() в нескольких местах, который должен стать secureSocket_.lowest_layer().is_open() для другого сокета.)

Может кто-нибудь предложить лучший способ сделать это?Я бы предпочел не создавать отдельный класс только для этой цели, потому что это означало бы дублирование большого количества кода.

Редактировать: Я перефразировал свой исходный вопрос, потому что неправильно понял назначение функции OpenSSL.

Ответы [ 4 ]

18 голосов
/ 09 января 2012

Я довольно поздно отвечаю на этот вопрос, но я надеюсь, что это поможет другим. Ответ Сэма содержит зародыш идеи, но, на мой взгляд, не уходит слишком далеко.

Идея возникла из наблюдения, что 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));
    }
};

Два подкласса обертывают сокеты 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();
    }
};

и

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();
    }
};

Каждый раз, когда вы вызываете функцию 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));

Обратите внимание, что сокет хранится как указатель. Я не могу думать, как иначе полиморфизм может быть скрыт.

Наказанием (которое я не считаю хорошим) является дополнительный уровень косвенности, используемый для получения сокетов без SSL.

14 голосов
/ 18 января 2011

Есть несколько способов сделать это. В прошлом я делал что-то вроде

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

Который может довольно быстро запутаться с большим количеством 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_;
};

Затем создайте производный класс SecureSocket для работы с secureSocket_ вместо socket_. Я не думаю, что это будет дублировать много кода, и, вероятно, будет чище, чем if/else, когда вам нужно async_read или async_write.

3 голосов
/ 21 ноября 2015

Проблема, конечно, в том, что 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...
}

Эта функция будет работать с обычными tcp :: сокетами, а также с защищенными сокетами 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 ...
1 голос
/ 19 июня 2012

Это будет скомпилировано примерно так:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...