- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的服务器基于boost spawn echo服务器示例,并在this thread中进行了改进。真实的服务器很复杂,我做了一个更简单的服务器来显示问题:
服务器监听端口12345,从新连接接收0x4000字节数据。
客户端运行1000个线程,连接到服务器并发送0x4000字节数据。
问题:当客户端运行时,1秒后通过控制台中的Ctrl-C杀死客户端进程,然后是服务器的io_context
将被停止,服务器运行到无限循环并消耗 100% 的 cpu。如果这种情况没有发生,请重复启动客户端并终止它几次,它就会发生。也许几次后它就会耗尽 TCP 端口,只需等待几分钟然后重试,在我的机器上杀死客户端 3~15 次后就会发生这种情况。
boost document说 io_context.stopped()
用于确定它是否已停止
either through an explicit call to stop(), or due to running out of work
我从不调用io_context.stop()
,并使用make_work_guard(io_context)
来保持io_context
不停止,但为什么会这样还是停了?
我的环境:Win10-64bit,boost 1.71.0
服务器代码:
#include <iostream>
using namespace std;
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
namespace ba=boost::asio;
#define SERVER_PORT 12345
#define DATA_LEN 0x4000
struct session : public std::enable_shared_from_this<session>
{
tcp::socket socket_;
boost::asio::steady_timer timer_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
explicit session(boost::asio::io_context& io_context, tcp::socket socket)
: socket_(std::move(socket)),
timer_(io_context),
strand_(io_context.get_executor())
{ }
void go()
{
auto self(shared_from_this());
boost::asio::spawn(strand_, [this, self](boost::asio::yield_context yield)
{
spawn(yield, [this, self](ba::yield_context yield) {
timer_.expires_from_now(10s); // 10 second
while (socket_.is_open()) {
boost::system::error_code ec;
timer_.async_wait(yield[ec]);
// timeout triggered, timer was not canceled
if (ba::error::operation_aborted != ec) {
socket_.close();
}
}
});
try
{
// recv data
string packet;
// read data
boost::system::error_code ec;
ba::async_read(socket_,
ba::dynamic_buffer(packet),
ba::transfer_exactly(DATA_LEN),
yield[ec]);
if(ec) {
throw "read_fail";
}
}
catch (...)
{
cout << "exception" << endl;
}
timer_.cancel();
socket_.close();
});
}
};
struct my_server {
my_server() { }
~my_server() { }
void start() {
ba::io_context io_context;
auto worker = ba::make_work_guard(io_context);
ba::spawn(io_context, [&](ba::yield_context yield)
{
tcp::acceptor acceptor(io_context,
tcp::endpoint(tcp::v4(), SERVER_PORT));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.async_accept(socket, yield[ec]);
if (!ec) {
std::make_shared<session>(io_context, std::move(socket))->go();
}
}
});
// Run io_context on All CPUs
auto thread_count = std::thread::hardware_concurrency();
boost::thread_group tgroup;
for (auto i = 0; i < thread_count; ++i)
tgroup.create_thread([&] {
for (;;) {
try {
if (io_context.stopped()) { // <- this happens after killing Client process several times
cout << "io_context STOPPED, now server runs infinit loop with full cpu usage" << endl;
}
io_context.run();
}
catch(const std::exception& e) {
MessageBox(0, "This never popup", e.what(), 0);
}
catch(const boost::exception& e) {
MessageBox(0, "This never popup", boost::diagnostic_information(e).data(), 0);
}
catch(...) { MessageBox(0, "This never popup", "", 0); }
}
});
tgroup.join_all();
}
};
int main() {
my_server svr;
svr.start();
}
客户:
#include <iostream>
#include <random>
#include <thread>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using namespace std;
using boost::asio::ip::tcp;
namespace ba=boost::asio;
#define SERVER "127.0.0.1"
#define PORT "12345"
int main() {
boost::asio::io_context io_context;
static string data_0x4000(0x4000, 'a');
boost::thread_group tgroup;
for (auto i = 0; i < 1000; ++i)
tgroup.create_thread([&] {
for(;;) {
try {
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
boost::asio::connect(s, resolver.resolve(SERVER, PORT));
ba::write(s, ba::buffer(data_0x4000));
} catch (std::exception e) {
cout << " exception: " << e.what() << endl;
} catch (...) {
cout << "unknown exception" << endl;
}
}
});
tgroup.join_all();
return 0;
}
更新解决方法:
我猜问题发生在 io_context
和协程上,所以我尝试将不必要的 spawn
替换为 std::thread
,并且它有效, io_context
永远不会停止。但为什么还是会出现这个问题呢?
替换:
ba::spawn(io_context, [&](ba::yield_context yield)
{
tcp::acceptor acceptor(io_context,
tcp::endpoint(tcp::v4(), SERVER_PORT));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.async_accept(socket, yield[ec]);
if (!ec) {
std::make_shared<session>(io_context, std::move(socket))->go();
}
}
});
致:
std::thread([&]()
{
tcp::acceptor acceptor(io_context,
tcp::endpoint(tcp::v4(), SERVER_PORT));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.accept(socket, ec);
if (!ec) {
std::make_shared<session>(io_context, std::move(socket))->go();
}
}
}).detach();
最佳答案
即使进行了(非常)广泛的压力测试,我也无法在 Linux 上重现您的问题。
即使是硬终止客户端进程也没有显示出除了某些 session 按预期到达“EOF”消息之外的任何其他影响。
存在可用端口耗尽的问题,但这主要是因为您在客户端中重新连接的速度太快了。
跳出框框思考
std::cout
和/或 MessageBox
² 没有同步,并且 MSVC 的标准库不能很好地处理它? catch
处理程序无法正确捕获的异常?我不知道这是否相关,但 MSVC 确实有 SEH(结构化异常) ^如果您有兴趣,这里是对代码的一些细微调整。它添加了处理/建立的 session /连接的一些可视化。请注意,客户端
大部分未更改,但服务器
进行了一些更改,可能会激发您的灵感:
#include <iostream>
#include <iomanip>
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
namespace ba = boost::asio;
using boost::asio::ip::tcp;
using namespace std::literals;
#define SERVER_PORT 12345
#define DATA_LEN 0x4000
void MessageBox(int, std::string const& caption, std::string const& message, ...) {
std::cerr << caption << ": " << std::quoted(message) << std::endl;
}
struct session : public std::enable_shared_from_this<session>
{
tcp::socket socket_;
ba::steady_timer timer_;
ba::strand<ba::io_context::executor_type> strand_;
explicit session(ba::io_context& io_context, tcp::socket socket)
: socket_(std::move(socket)),
timer_(io_context),
strand_(io_context.get_executor())
{ }
void go()
{
auto self(shared_from_this());
ba::spawn(strand_, [this, self](ba::yield_context yield)
{
spawn(yield, [this, self](ba::yield_context yield) {
while (socket_.is_open()) {
timer_.expires_from_now(10s);
boost::system::error_code ec;
timer_.async_wait(yield[ec]);
// timeout triggered, timer was not canceled
if (ba::error::operation_aborted != ec) {
socket_.close(ec);
}
}
});
try
{
// recv data
std::string packet;
// read data
ba::async_read(socket_,
ba::dynamic_buffer(packet),
ba::transfer_exactly(DATA_LEN),
yield);
std::cout << std::unitbuf << ".";
}
catch (std::exception const& e) {
std::cout << "exception: " << std::quoted(e.what()) << std::endl;
}
catch (...) {
std::cout << "exception" << std::endl;
}
boost::system::error_code ec;
timer_.cancel(ec);
socket_.close(ec);
});
}
};
struct my_server {
void start() {
ba::io_context io_context;
auto worker = ba::make_work_guard(io_context);
ba::spawn(io_context, [&](ba::yield_context yield)
{
tcp::acceptor acceptor(io_context,
tcp::endpoint(tcp::v4(), SERVER_PORT));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.async_accept(socket, yield[ec]);
if (!ec) {
std::make_shared<session>(io_context, std::move(socket))->go();
}
}
});
// Run io_context on All CPUs
auto thread_count = std::thread::hardware_concurrency();
boost::thread_group tgroup;
for (auto i = 0u; i < thread_count; ++i)
tgroup.create_thread([&] {
for (;;) {
try {
io_context.run();
break;
}
catch(const std::exception& e) {
MessageBox(0, "This never popup", e.what(), 0);
}
catch(const boost::exception& e) {
MessageBox(0, "This never popup", boost::diagnostic_information(e).data(), 0);
}
catch(...) { MessageBox(0, "This never popup", "", 0); }
}
std::cout << "stopped: " << io_context.stopped() << std::endl;
});
tgroup.join_all();
}
};
int main() {
my_server svr;
svr.start();
}
#include <iostream>
#include <random>
#include <thread>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
namespace ba=boost::asio;
#define SERVER "127.0.0.1"
#define PORT "12345"
int main() {
ba::io_context io_context;
static std::string const data_0x4000(0x4000, 'a');
boost::thread_group tgroup;
for (auto i = 0; i < 1000; ++i)
tgroup.create_thread([&] {
for(;;) {
try {
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
ba::connect(s, resolver.resolve(SERVER, PORT));
s.set_option(ba::socket_base::reuse_address(true));
ba::write(s, ba::buffer(data_0x4000));
} catch (std::exception const& e) {
std::cout << " exception: " << e.what() << std::endl;
} catch (...) {
std::cout << "unknown exception" << std::endl;
}
std::cout << std::unitbuf << ".";
}
});
tgroup.join_all();
}
² 也许 MessageBox
只允许来自“UI”线程。
关于c++ - 为什么 io_context 在我的 boost asio 协程服务器中被浸透,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58900039/
尝试实例化类的新对象时出现此错误。代码是: using boost::asio::ip::tcp; typedef boost::asio::io_service ioservice; class c
目前,我正在尝试将通过 post 或 dispatch 排队的工作移除到 io_context 中。工作由少量队列组排队,这些工作应立即全部移除: boost::asio::io_context co
由于最新版本的 boost,asio 推出了新的执行器并提供了 asio::strand .所以现在完全可以使用 asio::strand而不是 io_context::strand .但它们不能互换
我一直在使用 boost asio 库,其中大多数对象都需要 io_context 对象作为构造函数的参数。我已经阅读了 io_context 对象,根据文档,它指出它是 The io_context
在检查 1.66.0 版本中 boost::asio 的文档时,我注意到 io_context 构造函数提供了一个 concurrency_hint。范围。阅读文档后,我不确定是否可以使用 BOOST
我正在尝试在多个线程中使用 asio::io_context。 #include #include #include #include #include #include int main
我正在尝试使用 Boost Libraries 构建聊天室。但是当我尝试使用 asio::io_context 时,编译器说: io_context is not an member of asio.
我目前正在编写一个多线程服务器,其中每个线程都有一个 io_context 和一个要执行的任务对象列表,每个任务对象都有一个关联的 ip::tcp::socket 对象。 对于负载平衡,我有时会将任务
我有一个使用boost.beast实现的RESTServer.hpp,如下所示。 #pragma once #include #include #include #include #inclu
我正在使用 c++ 的 boost asio 库。我发现io_service和io_context有相似之处。例如,两者都有方法 run 和其他方法。有人可以详细说明这两个类之间的差异(如用法、概念思
此代码调用已发布的句柄 boost::asio::io_context ioc; boost::asio::post(ioc, []{ std::cout : boost::asio:
boost::asio::io_context::run() 确实在没有待处理的工作时返回。我想避免这种行为,这样 run() 就会无限期地等待新作品,并有可能从另一个线程停止它。 我想这可以通过在
我需要获得同步 I/O 但具有以下特性: 被其他线程中断 支持超时 因此,我使用来自 Boost.Asio 的异步 I/O 并通过 boost::asio::io_context::run_one_f
我在 中使用 boost::beast::websocket 和 boost::asio::io_context 编写了一个小型 websocket 客户端>C++。我有一个具有以下状态的状态机: e
据我所知,据我检查了 boost::asio 文档和源代码,除了销毁上下文本身之外,没有办法显式销毁给定 io_context 上所有挂起的处理程序吗? 如果可能的话,我需要能够停止 io_conte
我有 2 个 boost::asio::io_context 变量,一个用于我的 Raspberry Pi 和我的 arduino 之间的连接,另一个用于 Raspberry Pi 和客户端之间通过
我目前正在做一个使用 MQTT 协议(protocol)进行通信的项目。 专用文件中有一个 Session 类,它基本上只是设置发布处理程序,即当此客户端收到消息时调用的回调(处理程序检查主题是否匹配
我知道 epoll 和 io_context 是异步工作的。那么,你能告诉我这两者有什么区别吗? 你在 asio::io_context 中使用 epoll 吗? 最佳答案 POSIX 为我们提供了一
我的服务器基于boost spawn echo服务器示例,并在this thread中进行了改进。真实的服务器很复杂,我做了一个更简单的服务器来显示问题: 服务器监听端口12345,从新连接接收0x4
和 asio::thread_pool 有什么区别和一个 asio::io_context谁的run()函数是从多个线程调用的?我可以更换我的 boost::thread_group调用 io_con
我是一名优秀的程序员,十分优秀!