我正在尝试将着名的boost ssl client/server连接示例合并到一个程序中。对于您的类型参考,基类是这样的:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
namespace bt
{
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2011 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)
//
enum { max_length = 1024 };
class client
{
public:
client(boost::asio::io_service& io_service, boost::asio::ssl::context& context,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context)
{
boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
socket_.lowest_layer().async_connect(endpoint,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
void handle_connect(const boost::system::error_code& error,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
std::cout << "handle_connect\n";
if (!error)
{
std::cout << "handle_connect No error\n";
socket_.async_handshake(boost::asio::ssl::stream_base::client,
boost::bind(&client::handle_handshake, this,
boost::asio::placeholders::error));
}
else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
{
std::cout << "handle_connect retry!\n";
socket_.lowest_layer().close();
boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
socket_.lowest_layer().async_connect(endpoint,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
else
{
std::cout << "Connect failed: " << error << "\n";
}
}
void handle_handshake(const boost::system::error_code& error)
{
std::cout << "client handle_handshake\n";
if (!error)
{
std::cout << "Enter message: ";
// std::cin.getline(request_, max_length);
sprintf(request_, "%s", "Hi Testing...");
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));
}
else
{
std::cout << "Handshake failed: " << error << "\n";
}
}
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 << "\n";
}
}
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";
}
else
{
std::cout << "Read failed: " << error << "\n";
}
}
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
char request_[max_length];
char reply_[max_length];
};
//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2011 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)
//
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
class session
{
public:
session(boost::asio::io_service& io_service, boost::asio::ssl::context& context)
: socket_(io_service, context)
{
}
ssl_socket::lowest_layer_type& socket()
{
return socket_.lowest_layer();
}
void start()
{
std::cout << "session start->handshake\n";
socket_.async_handshake(boost::asio::ssl::stream_base::server,
boost::bind(&session::handle_handshake, this,
boost::asio::placeholders::error));
}
void handle_handshake(const boost::system::error_code& error)
{
std::cout << "session handle_handshake\n";
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
context_(io_service, boost::asio::ssl::context::sslv23)
{
//std::cout << "server()\n";
context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
context_.set_password_callback(boost::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.crt");
context_.use_private_key_file("server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");
session* new_session = new session(io_service_, context_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
std::string get_password() const
{
return "test";
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
std::cout << "server() handle_accept\n";
if (!error)
{
std::cout << "server() handle_accept !error\n";
new_session->start();
new_session = new session(io_service_, context_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
std::cout << "server() handle_accept error:" << error.message() << std::endl;
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
boost::asio::ip::tcp::acceptor acceptor_;
boost::asio::ssl::context context_;
};
}//namespace bt
主程序是:
BOOST_AUTO_TEST_CASE(accept_ssl_connection_1)
{
boost::asio::io_service io_service_1;
boost::asio::io_service io_service_2;
int port = random_port();
std::stringstream i("");
i << port;
std::cout << "Port is:" << i.str() << std::endl;
//server
bt::server(io_service_1, port);
//client
boost::asio::ip::tcp::resolver resolver(io_service_2);
boost::asio::ip::tcp::resolver::query query("127.0.0.1", i.str());
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ssl::context ctx(io_service_2, boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_peer);
ctx.load_verify_file("server.crt");
bt::client c(io_service_2, ctx, iterator);
boost::thread thread1(boost::bind(&boost::asio::io_service::run, &io_service_1));
boost::thread thread2(boost::bind(&boost::asio::io_service::run, &io_service_2));
thread1.join();
thread2.join();
}
这是我得到的输出:
Port is:7200
server() handle_accept
handle_connect
Connect failed: system:111
server() handle_accept error:Operation canceled
如果clien和server是单独构建和运行的,该程序可以正常工作。我想我的io_service使用有误。 你能帮我解决一下这个问题吗?
答案 0 :(得分:4)
我建议你加倍努力使代码可读。
代码供人类阅读,而不是计算机
在你的情况下,极端简洁,如
bt::client c(...);
导致像
这样的错误bt::server(io_service_1, port);
与 - 可能是预期的 - 变量声明
没有太大区别bt::server s(io_service_1, port);
否则,新构建的服务器立即被破坏,从而取消所有待处理的操作。
尝试实际呈现可读消息:
std::cout << "Connect failed: " << error.message() << "\n";
std::cout << "Handshake failed: " << error.message() << "\n";
std::cout << "Write failed: " << error.message() << "\n";
std::cout << "Read failed: " << error.message() << "\n";
std::cout << "server() handle_accept error:" << error.message() << std::endl;
这会告诉你“125”意味着“操作中止”等。这就是让我在这里和那里添加一点痕迹的原因:
~session() { std::cout << "Deleting session!\n"; }
~server() { std::cout << "Deleting server!\n"; }
而不是手动操作,更喜欢boost:
中定义的组合操作client(ba::io_service &io_service, ssl::context &context, tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context)
{
ba::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&client::handle_connect, this, bap::error));
}
void handle_connect(const boost::system::error_code &error) {
std::cout << "handle_connect\n";
if (!error) {
std::cout << "handle_connect No error\n";
socket_.async_handshake(ssl::stream_base::client, boost::bind(&client::handle_handshake, this, bap::error));
} else {
std::cout << "Connect failed: " << error.message() << "\n";
}
}
这是整个迭代者的舞蹈。但不易出错。
使用命名空间别名来获取可读/可管理的行
using boost::asio::ip::tcp;
namespace ba = boost::asio;
namespace bap = boost::asio::placeholders;
namespace ssl = boost::asio::ssl;
使用智能指针(delete this
?ugh)
考虑使用1个io_service。使用两个并不会添加任何东西,实际上,名称并没有澄清一件事。事实上,盯着你的代码的第一分钟让我解释了客户端和服务器的代码,煞费苦心地验证他们没有错误地使用错误的服务,导致过早run()
完成。
考虑比赛条件。在您的代码中,服务器和客户端独立运行不同步。在至少添加延迟:
boost::this_thread::sleep_for(boost::chrono::seconds(1));
避免client
在开始接受连接之前连接到server
。
首选boost::thread_group
丢失线程:
boost::thread_group tg;
// ...
tg.create_thread(boost::bind(&ba::io_service::run, &io_service_1));
// ...
tg.create_thread(boost::bind(&ba::io_service::run, &io_service_2));
// ...
tg.join_all();
事实上,使用1个io_service和1个线程,您可以回避以上所有问题(由于implicit strand而异步操作已同步)
使用更高级别的标准库功能(例如std::to_string(int)
而不是std::ostringstream
;如果您不能使用c ++ 11,请使用boost::lexical_cast
或编写您自己的to_string
-type helper function)。
如果地址被硬编码为环回,则无需“解析”任何内容:只需连接到tcp::endpoint{{}, port}
考虑将ctx
移至client
(就像您将server
的ssl参数移动到该类中一样)
更喜欢原始阵列上的boost :: array / std :: array(request_
和reply_
)
为什么要读取发送的字节数?你的意思是
ba::async_read(socket_, ba::buffer(reply_, bytes_transferred),
boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred));
我期待像
这样的东西ba::async_read(socket_, ba::buffer(reply_, reply.size()), // assuming array<>, see previous
boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred));
再次考虑对read_some
的合并操作。 read_some
可能无法阅读完整的请求。考虑添加成帧协议或预先发送请求长度。
避免代码重复:async_accept
编码两次。而是将它作为一个单独的函数并调用它两次:
void do_accept() {
session::ptr new_session = boost::make_shared<session>(io_service_, context_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, bap::error));
}
为接受添加截止日期,以便我们可以在某个空闲时间间隔停止服务器
由于您现在正在使用智能指针(不是吗?),也很容易在此处添加会话关闭(session::close()
)
让我们以一个人的价格做两个客户,只是为了好玩
<强> Live On Coliru 强>
//#define BOOST_ASIO_ENABLE_HANDLER_TRACKING 1
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <cstdlib>
#include <iostream>
using boost::asio::ip::tcp;
namespace ba = boost::asio;
namespace bap = boost::asio::placeholders;
namespace ssl = boost::asio::ssl;
namespace bt {
enum { max_length = 1024, idle_timeout_seconds = 2 };
class client {
public:
client(ba::io_service &io_service, tcp::resolver::iterator endpoint_iterator, std::string const& request)
: ctx_(io_service, ssl::context::sslv23),
socket_(io_service, ctx_),
request_(request)
{
ctx_.set_verify_mode(ssl::context::verify_peer);
ctx_.load_verify_file("server.crt");
ba::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&client::handle_connect, this, bap::error));
}
void handle_connect(const boost::system::error_code &error) {
std::cout << "handle_connect\n";
if (!error) {
std::cout << "handle_connect No error\n";
socket_.async_handshake(ssl::stream_base::client, boost::bind(&client::handle_handshake, this, bap::error));
} else {
std::cout << "Connect failed: " << error.message() << "\n";
}
}
void handle_handshake(const boost::system::error_code &error) {
std::cout << "client handle_handshake\n";
if (!error) {
ba::async_write(socket_, ba::buffer(request_),
boost::bind(&client::handle_write, this, bap::error, bap::bytes_transferred));
} else {
std::cout << "Handshake failed: " << error.message() << "\n";
}
}
void handle_write(const boost::system::error_code &error, size_t bytes_transferred) {
if (!error) {
ba::async_read(socket_, ba::buffer(reply_, bytes_transferred),
boost::bind(&client::handle_read, this, bap::error, bap::bytes_transferred));
} else {
std::cout << "Write failed: " << error.message() << "\n";
}
}
void handle_read(const boost::system::error_code &error, size_t bytes_transferred) {
if (!error) {
std::cout << "Reply: ";
std::cout.write(reply_.data(), bytes_transferred);
std::cout << "\n";
} else {
std::cout << "Read failed: " << error.message() << "\n";
}
}
private:
ssl::context ctx_;
ssl::stream<tcp::socket> socket_;
std::string request_;
std::array<char, max_length> reply_;
};
class session : public boost::enable_shared_from_this<session> {
public:
using ptr = boost::shared_ptr<session>;
session(ba::io_service &io_service, ssl::context &context) : socket_(io_service, context) {}
typedef ssl::stream<tcp::socket> ssl_socket;
ssl_socket::lowest_layer_type &socket() { return socket_.lowest_layer(); }
void start() {
std::cout << "session start->handshake\n";
socket_.async_handshake(ssl::stream_base::server, boost::bind(&session::handle_handshake, shared_from_this(), bap::error));
}
void handle_handshake(const boost::system::error_code &error) {
std::cout << "session handle_handshake\n";
if (error) return;
socket_.async_read_some(ba::buffer(data_),
boost::bind(&session::handle_read, shared_from_this(), bap::error, bap::bytes_transferred));
}
void handle_read(const boost::system::error_code &error, size_t bytes_transferred) {
if (error) return;
ba::async_write(socket_, ba::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, shared_from_this(), bap::error));
}
void handle_write(const boost::system::error_code &error) {
if (error) return;
socket_.async_read_some(ba::buffer(data_),
boost::bind(&session::handle_read, shared_from_this(), bap::error, bap::bytes_transferred));
}
void close() {
socket_.get_io_service().post([this] {
std::cout << "session::close()\n";
socket_.lowest_layer().cancel();
socket_.lowest_layer().close();
});
}
~session() { std::cout << "Deleting session\n"; }
private:
ssl_socket socket_;
std::array<char, max_length> data_;
};
class server {
public:
server(ba::io_service &io_service, unsigned short port)
: io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
context_(io_service, ssl::context::sslv23),
deadline_(io_service)
{
// std::cout << "server()\n";
context_.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::single_dh_use);
context_.set_password_callback(boost::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.crt");
context_.use_private_key_file("server.crt", ssl::context::pem);
context_.use_tmp_dh_file("dh2048.pem");
do_accept();
}
~server() { std::cout << "Deleting server\n"; }
std::string get_password() const { return "test"; }
void do_accept() {
session::ptr new_session = boost::make_shared<session>(io_service_, context_);
deadline_.expires_from_now(boost::posix_time::seconds(idle_timeout_seconds));
deadline_.async_wait(boost::bind(&server::handle_deadline, this, bap::error()));
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, bap::error));
}
void handle_accept(session::ptr new_session, const boost::system::error_code &error) {
std::cout << "server() handle_accept\n";
if (!error) {
std::cout << "server() handle_accept ok\n";
sessions_.push_back(new_session);
new_session->start();
do_accept();
} else {
std::cout << "server() handle_accept error:" << error.message() << std::endl;
}
}
void handle_deadline(boost::system::error_code ec) {
if (!ec) {
io_service_.post([this] {
// assuming 1 thread runs io_service, no more locking required
std::cout << "server() shutdown after idle timeout\n";
acceptor_.cancel();
acceptor_.close();
for (auto weak_sess : sessions_)
if (auto sess = weak_sess.lock())
sess->close();
});
}
}
private:
ba::io_service &io_service_;
tcp::acceptor acceptor_;
ssl::context context_;
ba::deadline_timer deadline_;
std::vector<boost::weak_ptr<session> > sessions_;
};
} // namespace bt
void accept_ssl_connection_1() {
ba::io_service svc;
int port = 6767;
std::cout << "Port is:" << port << std::endl;
// server
bt::server s(svc, port);
// client
tcp::resolver resolver(svc);
bt::client c(svc, resolver.resolve({"127.0.0.1", std::to_string(port)}), "Hello, I'm Bob");
bt::client d(svc, resolver.resolve({"127.0.0.1", std::to_string(port)}), "Hello, I'm Cindy");
svc.run();
}
int main() {
accept_ssl_connection_1();
}
打印
Port is:6767
server() handle_accept
server() handle_accept ok
session start->handshake
handle_connect
handle_connect No error
handle_connect
handle_connect No error
server() handle_accept
server() handle_accept ok
session start->handshake
session handle_handshake
client handle_handshake
session handle_handshake
client handle_handshake
Reply: Hello, I'm Bob
Reply: Hello, I'm Cindy
server() shutdown after idle timeout
server() handle_accept
server() handle_accept error:Operation canceled
Deleting session
session::close()
session::close()
Deleting session
Deleting session
Deleting server
答案 1 :(得分:1)
错误代码111(ECONNREFUSED)表示(在Linux中):
“目标地址没有监听连接或拒绝连接 连接请求。“
通常在客户端尝试连接到服务器时发生,并且没有人正在侦听端口。可能的原因:
在您的情况下,问题可能是选项#3。因为几乎在同一时间启动客户端和服务器时遇到问题。
我没有检查你的所有代码,客户端是否真的可以在服务器准备好之前尝试连接。