Я думаю, в библиотеке есть место для улучшения. Если вы прочитаете класс ip::tcp::iostream
(т.е. basic_socket_iostream<ip::tcp>
), вы увидите, что у него есть два базовых класса:
private detail::socket_iostream_base<ip::tcp>
public std::basic_iostream<char>
Первый содержит basic_socket_streambuf<ip::tcp>
(производный класс std::streambuf
и basic_socket<ip::tcp>
), адрес которого передается последнему во время строительства.
В большинстве случаев basic_socket_streambuf<ip::tcp>
выполняет фактические операции с сокетами через базовый класс basic_socket<ip::tcp>
. Однако есть функция-член connect_to_endpoints()
, которая переходит по абстракции и вызывает несколько низкоуровневых функций из пространства имен detail::socket_ops
непосредственно на socket().native_handle()
. (Похоже, это было введено в Git commit b60e92b13e .) Эти функции будут работать только на сокетах TCP, даже если класс является шаблоном для любого протокола.
До тех пор, пока я не обнаружил эту проблему, я планировал интегрировать поддержку SSL как iostream / streambuf, чтобы обеспечить класс протокола ssl
и специализацию шаблона basic_socket<ssl>
, чтобы обернуть существующие классы ssl::context
и ssl::stream<ip::tcp::socket>
. Примерно так (не скомпилируется):
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/basic_socket.hpp>
#include <boost/asio/ssl.hpp>
namespace boost {
namespace asio {
namespace ip {
class ssl
: public tcp // for reuse (I'm lazy!)
{
public:
typedef basic_socket_iostream<ssl> iostream;
// more things as needed ...
};
} // namespace ip
template <>
class basic_socket<ip::ssl>
{
class SslContext
{
ssl::context ctx;
public:
SslContext() : ctx(ssl::context::sslv23_client)
{
ctx.set_options(ssl::context::default_workarounds);
ctx.set_default_verify_paths();
}
ssl::context & context() { return ctx; }
} sslContext;
ssl::stream<ip::tcp::socket> sslSocket;
public:
explicit basic_socket(const executor & ex)
: sslSocket(ex, sslContext.context())
{}
executor get_executor() noexcept
{
return sslSocket.lowest_layer().get_executor();
}
void connect(const ip::tcp::endpoint & endpoint_)
{
sslSocket.next_layer().connect(endpoint_);
sslSocket.lowest_layer().set_option(ip::tcp::no_delay(true));
sslSocket.set_verify_mode(ssl::verify_peer);
sslSocket.set_verify_callback(
ssl::rfc2818_verification("TODO: pass the domain here through the stream/streambuf somehow"));
sslSocket.handshake(ssl::stream<ip::tcp::socket>::client);
}
void close()
{
sslSocket.shutdown();
sslSocket.next_layer().close();
}
};
} // namespace asio
} // namespace boost
Но из-за проблем с дизайном мне придется также специализировать basic_socket_streambuf<ip::ssl>
, чтобы избежать процедур detail::socket_ops
. (Я также должен избегать внедрения класса протокола ssl
в пространство имен boost::asio::ip
, но это побочная проблема.)
Не потратил много времени на это, но это кажется выполнимым. Первое исправление basic_socket_streambuf<>::connect_to_endpoints()
должно сильно помочь.