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

时间:2017-11-22 14:47:45

标签: c++ multithreading boost-asio

TL;博士

如何制作基于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()) - 采用完全不同的方式({{ 1}}处理单独的acceptor::async_accept()对象中包含的tcp::socket对象。

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

1 个答案:

答案 0 :(得分: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