Boost.Asio、tcp::iostream 和多线程

Posted

技术标签:

【中文标题】Boost.Asio、tcp::iostream 和多线程【英文标题】:Boost.Asio, tcp::iostream, and multithreading 【发布时间】:2017-11-22 14:47:44 【问题描述】:

tl;dr

如何使基于tcp::iostream 的服务器(如下面的代码)在不同的线程中接受多个连接?


我正在尝试使用 Boost.Asio 为预先存在的库实现服务器/客户端接口,沿着examples 前进。 (是的,C++11 之前的版本。请耐心等待。)

使用 tcp::iostream 的理由。

由于情况,整个服务器进程是可选的;即当使用port = 0 调用客户端时,它会将请求传递给本地Handler 实例。在其他所有情况下,它都会连接到希望在该端口号上侦听的服务器,然后服务器将请求传递给服务器进程的 Handler 实例。

(这都是严格的 127.0.0.1。服务器存在是因为多个 Handler 实例之间存在重要的共享状态,所以如果所有用户都将他们的请求发送到机器上运行的一台服务器,那么在内存和启动时间。“本地”,port = 0 选项存在,因为这样我就可以只使用一个客户端可执行文件,用于基于服务器的和本地使用。)

无论如何。有boost::asio::ip::tcp::iostream,让事情变得非常简单:

boost::asio::io_service io_service;

boost::asio::ip::tcp::endpoint endpoint( boost::asio::ip::tcp::v4(), port_ );
boost::asio::ip::tcp::acceptor acceptor( io_service, endpoint );

for (;;)

    boost::asio::ip::tcp::iostream stream;
    boost::system::error_code ec;
    acceptor.accept( *stream.rdbuf(), ec );
    if ( ! ec )
    
        Handler( stream, stream ).run(); // <---- This is the "why"
    
    else
    
        throw ec.message();
    
    stream.close();

你知道这是怎么回事。 Handler 类接受一个输入流和一个输出流。除了传递tcp::iostream,我可以传递std::cin/std::cout,或者一些指向请求文件和日志文件的std::fstream。这对我来说效果很好。

大“但是”

问题是上面的代码是严格的单线程的。由于Handler 完成的工作需要大量 时间(分钟),我需要多个工作线程。

以及所有关于“如何使 Boost.Asio 多线程”的示例和教程——例如HTTP 示例server2(使用单个io_service 对象池)和server3(使用多个线程,每个线程都调用io_service::run())——正在采用完全不同的方式(acceptor::async_accept() 处理tcp::socket 对象包含在单独的connection 对象中)。

将其归咎于我对 Boost.Asio 的整体架构的困惑,或者缺少文档,但我无法弄清楚如何将这两种方法结合起来,即有多个工作线程,每个工作线程都将其连接处理为 tcp::iostream .似乎没有办法从tcp::iostream 检索tcp::socket,也没有办法向它提供一个,此时我有点难过。

【问题讨论】:

您是否遇到过某个教程,我认为是在 gamedev 上?我可以帮你挖出来,但几年前我用它来做 io_service / tcp 的事情。据我回忆,io_service/run 是一种创建一堆线程的方法,它可以做任何你喜欢做的事情,但要与 asio 的各种套接字等一起使用。 gamedev.net/blog/950/… 找到了,正好在我的收藏夹中 @Carlos 再添加 1 个教程有什么帮助? @DevSolar,如何为每个处理程序启动一个线程并将 iostream 或套接字传递给它? @sehe:但我需要一个单独的 tcp::iostream 为每个Handler,不是吗? 【参考方案1】:

我想我在这里巧妙地遗漏了这个问题。与Asio无关:

Live C++03 On Coliru

#define BOOST_THREAD_USES_MOVE
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/asio.hpp>
#include <boost/asio/basic_socket_iostream.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <list>

struct Handler 
    std::istream& is;
    std::ostream& os;

    Handler(std::istream& is, std::ostream& os) : is(is), os(os)
    void run() 
        std::cout << __PRETTY_FUNCTION__ << ":" << __LINE__ << std::endl;
        std::string line;

        while (getline(is, line)) 
            std::reverse(line.begin(), line.end());
            os << line << std::endl;
        
    
;

struct SocketRequest : boost::enable_shared_from_this<SocketRequest> 
    boost::asio::ip::tcp::iostream stream;

    void start() 
        boost::async(boost::launch::async, boost::bind(&SocketRequest::do_run, shared_from_this()));
    

  private:
    void do_run() 
        return Handler(stream, stream).run();
    
;

int main() 
    boost::asio::io_service io_service;

    unsigned short port_ = 6767;
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port_);
    boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);

    for (;;)
    
        boost::shared_ptr<SocketRequest> req = boost::make_shared<SocketRequest>();

        acceptor.accept(*req->stream.rdbuf());
        req->start();
    

对于示例客户:

(for req in HELLO BYE; do sleep 1; netcat 127.0.0.1 6767 <<< "$req"; done)&

打印

void Handler::run():16
OLLEH
void Handler::run():16
EYB

【讨论】:

我在这里使用 c++11 线程来提高速度,显然你会使用 Boost Thread 来实现 c++03 兼容性 很抱歉问你这个问题,但你能把它变成使用 C++03 / Boost.Thread 的代码吗?看着(也不熟悉的)std::future 部分穿插着我正在努力掌握的 Boost.Asio 代码,这让我对过于密集而无法“获取”它感到更加沮丧。 或者,换一种说法——Boost 文档中的所有多线程示例都是关于多次调用io_service::run()。您的示例没有调用io_service::run(),甚至一次。我对代码看得很清楚,但我没有掌握它背后的概念。 :-( Hah - C++03 中的 Boost Future 和 Boost Move 很复杂。我还不知道如何使用BOOST_MOVE_RET,但在你的情况下,你可能不关心回报:这是一个开始coliru.stacked-crooked.com/a/9c2337e92446511d io_service::run() 供异步使用

以上是关于Boost.Asio、tcp::iostream 和多线程的主要内容,如果未能解决你的问题,请参考以下文章

boost :: asio :: ip :: tcp :: iostream,首先启动客户端并等待服务器?

将 fork() 与 boost::asio::ip::tcp::iostream 一起使用是不是安全?

使用 boost::asio::async_wait_until 和 boost::asio::streambuf

boost asio 学习 boost::asio 网络封装

Boost::Asio入门剖析

websocketpp 和 boost.asio 有啥区别?