【问题标题】:Persistent ASIO connections持久的 ASIO 连接
【发布时间】:2013-04-10 07:07:30
【问题描述】:

我正在从事一个项目,我需要能够使用一些持久性来长时间与不同的服务器通信。该服务器将具有相当高的吞吐量。我无法找到正确设置持久连接的方法。我能想到的最好方法是创建一个持久连接类。理想情况下,我会连接到服务器一次,并在信息进入我时执行 async_writes。并在信息返回给我时阅读信息。不过,我认为我的课程结构不正确。

这是我现在构建的:

persistent_connection::persistent_connection(std::string ip, std::string port):
    io_service_(), socket_(io_service_), strand_(io_service_), is_setup_(false), outbox_()
{
    boost::asio::ip::tcp::resolver resolver(io_service_);
    boost::asio::ip::tcp::resolver::query query(ip,port);
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
    boost::asio::ip::tcp::endpoint endpoint = *iterator;
    socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator));
    io_service_.poll();
}

void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
    if(ec)
    {
        std::cout << "Couldn't connect" << ec << std::endl;
        return;
    }
    else
    {
        boost::asio::socket_base::keep_alive option(true);
        socket_.set_option(option);
        boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n", boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
    }
}

void persistent_connection::write(const std::string &message)
{
    write_impl(message);
    //strand_.post(boost::bind(&persistent_connection::write_impl, this, message));
}

void persistent_connection::write_impl(const std::string &message)
{
    outbox_.push_back(message);
    if(outbox_.size() > 1)
    {
        return;
    }
    this->write_to_socket();
}

void persistent_connection::write_to_socket()
{
    std::string message = "GET /"+ outbox_[0] +" HTTP/1.0\r\n";
    message += "Host: 10.1.10.120\r\n";
    message += "Accept: */*\r\n";
    boost::asio::async_write(socket_, boost::asio::buffer(message.c_str(), message.size()), strand_.wrap(
                             boost::bind(&persistent_connection::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void persistent_connection::handle_write(const boost::system::error_code& ec, std::size_t bytes_transfered)
{
    outbox_.pop_front();
    if(ec)
    {
        std::cout << "Send error" << boost::system::system_error(ec).what() << std::endl;
    }
    if(!outbox_.empty())
    {
        this->write_to_socket();
    }
    boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n",boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}

我将从这里发送的第一条消息似乎可以正常发送,服务器得到它,并以有效的响应进行响应。不幸的是,我看到了两个问题:

1) 在执行 async_write 命令后,我的 handle_write 永远不会被调用,我不知道为什么。 2)程序从不读取响应,我猜这与#1有关,因为在该函数发生之前不会调用 asyn_read_until 。 3) 我还想知道是否有人可以告诉我为什么我注释掉的 strand_.post 调用不起作用。

我猜这大部分是由于我不知道应该如何使用我的 io_service,所以如果有人能给我任何指点,我将不胜感激。如果您需要任何其他信息,我很乐意提供更多信息。

谢谢

编辑调用写入:

int main()
{
    persistent_connection p("10.1.10.220", "80");
    p.write("100");
    p.write("200");
    barrier b(1,30000); //Timed mutex, waits for 300 seconds.
    b.wait();
}

void persistent_connection::handle_read_headers(const boost::system::error_code &ec)
{
    std::istream is(&buf_);
    std::string read_stuff;
    std::getline(is,read_stuff);
    std::cout << read_stuff << std::endl;
}

【问题讨论】:

  • 在您发布的代码中,您永远不会调用 write 并且读取处理程序不存在。你能提供吗?
  • 您是否在多个线程中调用您的io_service 中的run?因为如果没有,您将不需要股线。 if 您在多个线程上调用它,您应该使用 strand 来包装同一对象的每个处理程序,否则同一连接的处理程序可能会同时执行。
  • 我将从多个线程调用 write。我这个程序的结构是一个 asio 网络服务器。对于每个请求,我将解析信息,然后将解析后的信息通过持久连接发送出去。所以每个连接都会调用write,每个连接都会在不同的线程上。
  • 用我的 main 和 read 处理程序更新了 OP,很抱歉把它放在那里

标签: c++ http tcp boost-asio


【解决方案1】:

所描述的行为是 io_service_ 的事件循环不再被处理的结果。

构造函数调用io_service::poll(),它将运行准备运行且不会阻塞等待工作完成的处理程序,而io_service::run() 将阻塞直到所有工作完成。因此,在轮询时,如果连接的另一端没有写入任何数据,则可能没有任何处理程序准备好运行,执行将从poll()返回。

关于线程,如果每个连接都有自己的线程,并且通信是半双工协议,比如HTTP,那么如果同步编写,应用程序代码可能会更简单。另一方面,如果每个连接都有自己的线程,但代码是异步编写的,那么请考虑处理从事件循环中抛出的异常。 Boost.Asio 的可能值得一读 effect of exceptions thrown from handlers.

另外,persistent_connection::write_to_socket() 引入了未定义的行为。调用boost::asio::async_write() 时,记录显示调用者保留缓冲区的所有权,并且必须保证缓冲区在调用处理程序之前保持有效。在这种情况下,message 缓冲区是一个自动变量,其生命周期可能在调用 persistent_connection::handle_write 处理程序之前结束。一种解决方案是通过将 message 设为成员变量来更改 message 的生命周期以匹配 persistent_connection 的生命周期。

【讨论】:

  • 我同步写这个的问题是我正在连接的服务器也可能需要长达 100 毫秒才能返回信息。因此,如果我在示例中发送两条消息,则消息 2 可能在消息 1 之前完成。我需要构建我的服务器以能够每秒处理数千个连接。所以考虑到这一点,我认为通过一个连接一次发送多个请求并接收信息,并匹配谁发送了带有 ID 的内容是可行的。这不是我应该如何使用 asio 套接字吗?
  • 如果每个连接都有自己的线程并使用半双工协议,那么从异步编程中获得的收益很少(如果有的话)。看起来代码试图通过 HTTP 执行全双工通信,这有其自身的复杂性。如果您计划每秒处理数千个连接,那么您可能需要考虑使用线程池和支持全双工通信的协议。
  • 好的,我想我明白你现在在说什么了。我想我正在以错误的方式解决这个问题。而不是我希望“流式传输”数据的几个持久连接,我最好建立足够的持久连接来处理我所有的出站请求,一次一个,并以同步方式处理它们。因此,如果我每 100 毫秒收到 10 个请求(并且响应延迟为 100 毫秒),我将需要 10 个持久连接来处理我的流量,我应该继续重用它们。
猜你喜欢
  • 2017-07-12
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 2011-01-16
  • 2013-01-22
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多