gpt4 book ai didi

c++ - 无法实现 boost::asio::ssl::stream 重新连接到服务器

转载 作者:搜寻专家 更新时间:2023-10-31 01:29:51 28 4
gpt4 key购买 nike

我需要实现一个处理连接到 ssl 服务器的类。差不多基于 this .然而。它没有 reconnect feature .所以我像这样修改它:

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(
^

然后我尝试取消引用指针以保留旧结构,但是出现了新错误:(

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,所以这件事对我来说很复杂。

最佳答案

这是我在 Boost 1.66.0 中对该演示所做的最小更改。请在 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.

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

这是现场演示:

enter image description here

进一步思考

根据您的偏执程度,在 do_reconnect() 中实际关闭 ssl 流可能会感觉更好:

boost::system::error_code ec;
socket_.shutdown(ec);
if (ec) std::cout << "shutdown error: " << ec.message() << std::endl;

这也行。您甚至可以决定终止任何最低级别的连接以防万一:

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

使用动态分配的套接字

如前所述,最纯粹的解决方案是不重用流/套接字对象:

boost::optional<stream> socket_;

现在,更新所有对间接socket_的引用,do_reconnect()可以变成:

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

//
// 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;
}

关于c++ - 无法实现 boost::asio::ssl::stream<boost::asio::ip::tcp::socket> 重新连接到服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49122521/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com