无法实现boost :: asio :: ssl :: stream< boost :: asio :: ip :: tcp :: socket>重新连接到服务器 [英] Can't implement boost::asio::ssl::stream<boost::asio::ip::tcp::socket> reconnect to server
问题描述
我需要实现一个处理连接到ssl服务器的类.差不多 基于此.然而.它没有重新连接功能.所以我 像这样修改它:
I need to implement a class which handle connect to ssl server. Pretty much based on this. However. it doesn't have reconnect feature. So I modify it like this:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
到
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> *mpSocket_;
并重构与->
但这会导致如下错误:
/usr/include/boost/asio/impl/read.hpp:271: error: request for member 'async_read_some' in '((boost::asio::detail::read_op<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::_bi::bind_t<void, boost::_mfi::mf2<void, SSLHandler, const boost::system::error_code&, long unsigned int>, boost::_bi::list3<boost::_bi::value<SSLHandler*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >*)this)->boost::asio::detail::read_op<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::_bi::bind_t<void, boost::_mfi::mf2<void, SSLHandler, const boost::system::error_code&, long unsigned int>, boost::_bi::list3<boost::_bi::value<SSLHandler*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >::stream_', which is of pointer type 'boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*' (maybe you meant to use '->' ?)
stream_.async_read_some(
^
/usr/include/boost/asio/impl/write.hpp:258: error: request for member 'async_write_some' in '((boost::asio::detail::write_op<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::_bi::bind_t<void, boost::_mfi::mf2<void, SSLHandler, const boost::system::error_code&, long unsigned int>, boost::_bi::list3<boost::_bi::value<SSLHandler*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >*)this)->boost::asio::detail::write_op<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::_bi::bind_t<void, boost::_mfi::mf2<void, SSLHandler, const boost::system::error_code&, long unsigned int>, boost::_bi::list3<boost::_bi::value<SSLHandler*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >::stream_', which is of pointer type 'boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*' (maybe you meant to use '->' ?)
stream_.async_write_some(
^
然后我尝试取消对指针的引用以保留旧结构,但是出现了新错误:(
Then I tried to dereference the pointer to keep the old structure but there's new error :(
boost::asio::async_connect(*socket_.lowest_layer(), mEndpointIterator, boost::bind(&SSLHandler::handle_connect, this, boost::asio::placeholders::error));
error: request for member 'lowest_layer' in '((SSLHandler*)this)->SSLHandler::socket_', which is of pointer type 'boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >*' (maybe you meant to use '->' ?)
boost::asio::async_connect(*socket_.lowest_layer(), mEndpointIterator, boost::bind(&SSLHandler::handle_connect, this, boost::asio::placeholders::error));`
请帮助,我来自Java,所以这件事对我来说相当复杂.
Please help, I'm coming from java so it's quite complicated with me this thing.
推荐答案
这是我对Boost 1.66.0中该演示的最小更改.请在github上单独查看补丁: https://github. com/boostorg/asio/compare/develop ... sehe:so-q49122521
This is my minimal change to that demo in Boost 1.66.0. See the patch in isolation on github: https://github.com/boostorg/asio/compare/develop...sehe:so-q49122521
注意:我将地址解析移到了连接序列中,因为如果更改了网络配置,结果可能会有所不同,或者应首选另一个端点.
Note: I moved the address resolve into the connect sequence, because if the network configuration has been changed, the result may differ, or another one of the endpoints should be preferred.
为此,我们存储了一个resolver::query query_
成员,以便我们可以在重新连接时重复该查询.
For this end, we store a resolver::query query_
member so we can repeat the query on reconnect.
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
enum { max_length = 1024 };
class client
{
public:
client(boost::asio::io_context& io_context,
boost::asio::ssl::context& context,
boost::asio::ip::tcp::resolver::query query)
: socket_(io_context, context), query_(query), timer_(io_context)
{
socket_.set_verify_mode(boost::asio::ssl::verify_peer);
socket_.set_verify_callback(
boost::bind(&client::verify_certificate, this, _1, _2));
start_connect();
}
void start_connect() {
boost::asio::ip::tcp::resolver r(socket_.get_io_context());
boost::asio::async_connect(socket_.lowest_layer(), r.resolve(query_),
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
void do_reconnect() {
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&client::handle_reconnect_timer, this, boost::asio::placeholders::error));
}
void handle_reconnect_timer(boost::system::error_code ec) {
if (!ec) {
start_connect();
}
}
bool verify_certificate(bool preverified,
boost::asio::ssl::verify_context& ctx)
{
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// In this example we will simply print the certificate's subject name.
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 " << subject_name << "\n";
return preverified;
}
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_handshake(boost::asio::ssl::stream_base::client,
boost::bind(&client::handle_handshake, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Connect failed: " << error.message() << "\n";
do_reconnect();
}
}
void accept_message() {
std::cout << "Enter message: ";
std::cin.getline(request_, max_length);
size_t request_length = strlen(request_);
boost::asio::async_write(socket_,
boost::asio::buffer(request_, request_length),
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_handshake(const boost::system::error_code& error)
{
if (!error)
{
accept_message();
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
do_reconnect();
}
}
void handle_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_read(socket_,
boost::asio::buffer(reply_, bytes_transferred),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
do_reconnect();
}
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
accept_message(); // continue using the same socket_ until fail
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
do_reconnect();
}
}
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::ip::tcp::resolver::query query_;
boost::asio::deadline_timer timer_;
char request_[max_length];
char reply_[max_length];
};
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: client <host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");
client c(io_context, ctx, {argv[1], argv[2]});
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
这里是现场演示:
根据您的偏执程度,您可能会觉得实际上关闭do_reconnect()
中的ssl流会更好:
Depending on your level of paranoia you could feel better actually closing the ssl stream in do_reconnect()
:
boost::system::error_code ec;
socket_.shutdown(ec);
if (ec) std::cout << "shutdown error: " << ec.message() << std::endl;
那也行.您甚至可以决定终止任何最低级别的连接,以防万一:
That also works. You could even decide to kill off any lowest level connection just in case:
auto& ll = socket_.lowest_layer();
if (ll.is_open())
{
ll.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
//if (ec) std::cout << "socket.shutdown error: " << ec.message() << std::endl;
ll.close(ec);
//if (ec) std::cout << "socket.close error: " << ec.message() << std::endl;
}
使用动态分配的套接字
如前所述,最纯粹的解决方案是不重用流/套接字对象:
Using a dynamically allocated socket
As mentioned, the purest solution would be to not reuse the stream/socket objects:
boost::optional<stream> socket_;
现在,将所有对间接socket_
,do_reconnect()
的引用更新为:
Now, updating all references to indirect socket_
, do_reconnect()
can become:
void do_reconnect() {
auto& io_context = socket_->get_io_context();
{
boost::system::error_code ec;
socket_->shutdown(ec);
if (ec) std::cout << "shutdown error: " << ec.message() << std::endl;
}
socket_.emplace(io_context, context_);
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&client::handle_reconnect_timer, this, boost::asio::placeholders::error));
}
显然也可以.
以下是相应的补丁程序: https://github.com/boostorg/asio/compare/develop...sehe:so-q49122521-dynamic
Here's the corresponding patch: https://github.com/boostorg/asio/compare/develop...sehe:so-q49122521-dynamic
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <iostream>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
enum { max_length = 1024 };
namespace ssl = boost::asio::ssl;
using tcp = boost::asio::ip::tcp;
class client
{
using stream = ssl::stream<tcp::socket>;
public:
client(boost::asio::io_context& io_context, ssl::context& context, tcp::resolver::query query)
: context_(context), socket_(boost::in_place_init, io_context, context_), query_(query), timer_(io_context)
{
socket_->set_verify_mode(ssl::verify_peer);
socket_->set_verify_callback(
boost::bind(&client::verify_certificate, this, _1, _2));
start_connect();
}
void start_connect() {
tcp::resolver r(socket_->get_io_context());
boost::asio::async_connect(socket_->lowest_layer(), r.resolve(query_),
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
void do_reconnect() {
auto& io_context = socket_->get_io_context();
{
boost::system::error_code ec;
socket_->shutdown(ec);
if (ec) std::cout << "shutdown error: " << ec.message() << std::endl;
}
socket_.emplace(io_context, context_);
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&client::handle_reconnect_timer, this, boost::asio::placeholders::error));
}
void handle_reconnect_timer(boost::system::error_code ec) {
if (!ec) {
start_connect();
}
}
bool verify_certificate(bool preverified,
ssl::verify_context& ctx)
{
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// In this example we will simply print the certificate's subject name.
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 " << subject_name << "\n";
return preverified;
}
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
socket_->async_handshake(ssl::stream_base::client,
boost::bind(&client::handle_handshake, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Connect failed: " << error.message() << "\n";
do_reconnect();
}
}
void accept_message() {
std::cout << "Enter message: ";
std::cin.getline(request_, max_length);
size_t request_length = strlen(request_);
boost::asio::async_write(*socket_,
boost::asio::buffer(request_, request_length),
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_handshake(const boost::system::error_code& error)
{
if (!error)
{
accept_message();
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
do_reconnect();
}
}
void handle_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_read(*socket_,
boost::asio::buffer(reply_, bytes_transferred),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
do_reconnect();
}
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
accept_message(); // continue using the same socket_ until fail
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
do_reconnect();
}
}
private:
ssl::context& context_;
boost::optional<stream> socket_;
tcp::resolver::query query_;
boost::asio::deadline_timer timer_;
char request_[max_length];
char reply_[max_length];
};
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: client <host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
ssl::context ctx(ssl::context::sslv23);
ctx.load_verify_file("ca.pem");
client c(io_context, ctx, {argv[1], argv[2]});
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
这篇关于无法实现boost :: asio :: ssl :: stream< boost :: asio :: ip :: tcp :: socket>重新连接到服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!