【问题标题】:Write:Unitialized Error when performing boost::asio::async_write写入:执行 boost::asio::async_write 时出现统一错误
【发布时间】:2015-12-03 04:21:47
【问题描述】:

我已经指定使用boost::asio 创建一个 HTTPS 服务器,所以我确实在互联网上花了一些时间,并找到了一个解释我们如何将 boost HTTP 及其 SSL 功能结合在一起的资源,这在 boost 中没有解释官方网站。一切顺利,现在我处于执行阶段,这就是一个令人作呕的问题出现的地方,在我构建请求流之后,我使用boost::asio::async_write 来交付它,在运行时我收到一个错误,例如下面,我很确定它是由boost::asio::async_write引起的,但我不确定是什么原因造成的,谁能帮我解释一下,我一直在黑暗中徘徊:((请看我的代码下面)

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
  what():  write: uninitialized

 using boost::asio::ip::tcp;
 string my_password_callback(size_t, boost::asio::ssl::context_base::password_purpose);
 void handle_resolve(const boost::system::error_code& ,
                        tcp::resolver::iterator);
 bool verify_certificate();
 void handle_read();
 void handle_write();



    int i,j,rc;
    sqlite3 *db;
    string selectsql;
    sqlite3_stmt *stmt;
    char *zErrMsg = 0;
    stringstream ss;
    boost::asio::io_service io_service1;
    boost::asio::io_service &io_service(io_service1);
    boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
    boost::asio::ssl::context& context_=ctx;
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_(io_service,context_);
int main()
{
    boost::shared_ptr<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
    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(my_password_callback);
    context_.use_certificate_chain_file("SSL\\test.crt");
    context_.use_private_key_file("SSL\\test.key", boost::asio::ssl::context::pem);
    tcp::resolver resolver_(io_service);
    tcp::resolver::query query("172.198.72.135:3000", "http");
    resolver_.async_resolve(query,boost::bind(handle_resolve,
                                           boost::asio::placeholders::error,
                                            boost::asio::placeholders::iterator));
    boost::asio::streambuf request;
    string path="https://172.198.72.135:3000/journals/enc_data?";
while(true)
{
    char * EJTEXT;
    int ID;
    if(sqlite3_open("c:\\MinGW\\test.db", &db))
    {
    selectsql="select IEJ,EJ from EJ limit 1";
    sqlite3_prepare_v2(db, selectsql.c_str(), -1, &stmt, NULL);
    if(sqlite3_step(stmt)==SQLITE_ROW){
    ID=sqlite3_column_int(stmt,0);
    EJTEXT=(char *)sqlite3_column_text(stmt,1);
    }
    else{

    }
   sqlite3_finalize(stmt);
   sqlite3_close(db);
    }
    string EJ=EJTEXT;
     E.Encrypt(EJ);
     string data=E.Url_safe(E.cipher);--my logic
     string Iv=E.Url_safe(E.encoded_iv);--my logic

     std::ostream request_stream(&request);
     request_stream << "POST " <<path+"Data="+data+"&"+"iv="+Iv;
     request_stream << "Host: " <<"172.198.72.135"<< "\r\n";
     request_stream << "Accept: */*\r\n";
     request_stream << "Connection: close\r\n\r\n";
    //try{
   boost::asio::async_write(socket_, request,
       boost::asio::transfer_at_least(1),
        boost::bind(handle_write));
temp="";
data="";
Iv="";

    boost::asio::streambuf response; 
    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    std::getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
      l.HTTP_SSLLOG("Invalid response");
    }
    if (status_code== 200)
    {
     string deletesql="delete * from EJ where IEJ="+ID;
     if(sqlite3_open("c:\\MinGW\\test.db", &db))
    {
    rc=sqlite3_exec(db, deletesql.c_str(), 0, 0, &zErrMsg);
    sqlite3_close(db);
    if(rc)
    {
        ss<<ID;
        l.EJ_Log("ERROR DELETING EJ FOR  "+ss.str());
    }
    }
    else{
        l.DB_Log("ERROR OPENING DB");
    }
    }
    else{
    continue;
}
Sleep(6000);
}
 return 0;
}

 string my_password_callback(size_t t, boost::asio::ssl::context_base::password_purpose p)//std::size_t max_length,ssl::context::password_purpose purpose )
   {
    std::string password;
    return "balaji";
   }

   void handle_resolve(const boost::system::error_code& err,
                        tcp::resolver::iterator endpoint_iterator)
    {
        if (!err)
        {
            socket_.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
            socket_.set_verify_callback(boost::bind(verify_certificate));
            boost::asio::connect(socket_.lowest_layer(), endpoint_iterator);
        }
        else
        {
            l.HTTP_SSLLOG("Error resolve: "+err.message());
        }
    }

    bool verify_certificate()
    {
    bool preverified =true;
    context_.set_default_verify_paths();
    return preverified;
    }

    void handle_read()
       {

       }
    void handle_write()
    {
     boost::asio::async_read_until(socket_, response, "\r\n",
          boost::bind(handle_read));


    }  

【问题讨论】:

标签: c++ boost https openssl boost-asio


【解决方案1】:

异步操作旨在不引发异常,而是将错误作为第一个参数传递给完成处理程序 (boost::system::error_code)。例如,以下程序 demonstrates async_write() 因未初始化错误而失败:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

int main()
{
  boost::asio::io_service io_service;
  boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
  boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service, ctx);  
  boost::asio::async_write(socket, boost::asio::buffer("demo"), 
    [](const boost::system::error_code& error, std::size_t bytes_transferred)
    {
      std::cout << error.message() << std::endl;
    });
  io_service.run();
}

上面的程序会输出uninitialized。如果异步操作抛出异常,则强烈建议调用未定义的行为。

根据发布的代码,async_write() 操作可能违反调用者保留底层缓冲存储器所有权的要求,调用者必须保证它在调用处理程序之前保持有效。在这种情况下,如果 while 循环的下一次迭代可能会使已提供给上一次迭代的 async_write() 操作的缓冲区无效。

但是,即使没有未定义的行为,也会出现其他问题,因为程序既不尝试建立连接也不执行 SSL 握手,这两者都必须在通过加密连接传输或接收数据之前完成。


在使用异步操作时,作为整个操作流程一部分的 while-sleep 循环通常表示代码异味。考虑删除 sqlite3 并加密代码,并首先启动并运行 SSL 原型。它还可能有助于在启用最高警告级别/迂腐标志的情况下进行编译。 Boost.Asio SSL overview 展示了典型的同步使用模式:

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;

// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();

// Open a socket and connect it to the remote host.
boost::asio::io_service io_service;
ssl_socket sock(io_service, ctx);
tcp::resolver resolver(io_service);
tcp::resolver::query query("host.name", "https");
boost::asio::connect(sock.lowest_layer(), resolver.resolve(query));
sock.lowest_layer().set_option(tcp::no_delay(true));

// Perform SSL handshake and verify the remote host's
// certificate.
sock.set_verify_mode(ssl::verify_peer);
sock.set_verify_callback(ssl::rfc2818_verification("host.name"));
sock.handshake(ssl_socket::client);

// ... read and write as normal ...

official SSL example 也可以作为使用异步操作的一个很好的起点或参考。一旦 SSL 原型被确认可以工作,然后将 sqlite3 和加密逻辑添加回程序中。

此外,如果正在使用多个线程,请注意 SSL 流不是线程安全的。所有异步操作都必须通过显式strand 同步。对于组合操作,例如async_write(),必须在strand 的上下文中调用启动函数,并且完成处理程序必须由相同的strand 包装。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    相关资源
    最近更新 更多