boost ssl连接过程失败

时间:2017-08-22 08:41:24

标签: c++ ssl boost boost-asio

我正在尝试将着名的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使用有误。 你能帮我解决一下这个问题吗?

2 个答案:

答案 0 :(得分:4)

1。式

我建议你加倍努力使代码可读。

  

代码供人类阅读,而不是计算机

在你的情况下,极端简洁,如

bt::client c(...);

导致像

这样的错误
bt::server(io_service_1, port);

与 - 可能是预期的 - 变量声明

没有太大区别
bt::server s(io_service_1, port);

否则,新构建的服务器立即被破坏,从而取消所有待处理的操作。

2。调试

尝试实际呈现可读消息:

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

2。 Asio Review,更多风格

  1. 而不是手动操作,更喜欢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";
        }
    }
    

    这是整个迭代者的舞蹈。但不易出错。

  2. 使用命名空间别名来获取可读/可管理的行

    using boost::asio::ip::tcp;
    namespace ba = boost::asio;
    namespace bap = boost::asio::placeholders;
    namespace ssl = boost::asio::ssl;
    
  3. 使用智能指针(delete this?ugh)

  4. 考虑使用1个io_service。使用两个并不会添加任何东西,实际上,名称并没有澄清一件事。事实上,盯着你的代码的第一分钟让我解释了客户端和服务器的代码,煞费苦心地验证他们没有错误地使用错误的服务,导致过早run()完成。

  5. 考虑比赛条件。在您的代码中,服务器和客户端独立运行不同步。在至少添加延迟:

    boost::this_thread::sleep_for(boost::chrono::seconds(1));
    

    避免client在开始接受连接之前连接到server

  6. 首选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();
    
  7. 事实上,使用1个io_service和1个线程,您可以回避以上所有问题(由于implicit strand而异步操作已同步)

  8. 使用更高级别的标准库功能(例如std::to_string(int)而不是std::ostringstream;如果您不能使用c ++ 11,请使用boost::lexical_cast或编写您自己的to_string -type helper function)。

  9. 如果地址被硬编码为环回,则无需“解析”任何内容:只需连接到tcp::endpoint{{}, port}

  10. 考虑将ctx移至client(就像您将server的ssl参数移动到该类中一样)

  11. 更喜欢原始阵列上的boost :: array / std :: array(request_reply_

  12. 为什么要读取发送的字节数?你的意思是

    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));
    
  13. 再次考虑对read_some的合并操作。 read_some可能无法阅读完整的请求。考虑添加成帧协议或预先发送请求长度。

  14. 避免代码重复: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));
    }
    
  15. 奖金

    1. 为接受添加截止日期,以便我们可以在某个空闲时间间隔停止服务器

    2. 由于您现在正在使用智能指针(不是吗?),也很容易在此处添加会话关闭(session::close()

    3. 让我们以一个人的价格做两个客户,只是为了好玩

    4. <强> 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中):

  

“目标地址没有监听连接或拒绝连接   连接请求。“

通常在客户端尝试连接到服务器时发生,并且没有人正在侦听端口。可能的原因:

  1. 服务器程序未运行
  2. 服务器程序使用与客户端不同的TCP端口号
  3. 服务器程序仍在启动。当客户端尝试连接时,端口尚未绑定。
  4. 在您的情况下,问题可能是选项#3。因为几乎在同一时间启动客户端和服务器时遇到问题。

    我没有检查你的所有代码,客户端是否真的可以在服务器准备好之前尝试连接。