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