使用协程提升 MSM + ASIO。一个简单的测试用例不起作用
Boost MSM + ASIO with coroutines. A Simple test case not working
我正在尝试构建一个结合文档中的两个提升示例的小示例。第一个是来自 MSM(state machines)库的示例:https://www.boost.org/doc/libs/1_75_0/libs/msm/doc/HTML/examples/AnonymousTutorialWithFunctors.cpp
第二个是来自 Asio 的 echo 服务器(带协程)示例:https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp
回显服务器示例 运行s 与我的 machine 正确。
状态 machine 有 2 个状态,AsioInitState
和 RegisterServersState
,在转换 table.
上只有一个匿名转换
按照我的源代码:
EchoServerMSM.h
#pragma once
// back-end
#include <boost/msm/back/state_machine.hpp>
//front-end
#include <boost/msm/front/state_machine_def.hpp>
// functors
#include <boost/msm/front/functor_row.hpp>
#include <boost/msm/front/euml/common.hpp>
// Asio
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace boost::msm::front;
using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::use_awaitable;
namespace this_coro = boost::asio::this_coro;
struct EchoServerSMFE : public msm::front::state_machine_def<EchoServerSMFE>
{
// The list of FSM states
struct AsioInitState : public msm::front::state<>
{
template <class Event, class FSM>
void on_entry(Event const&, FSM& fsm) {
std::cout << "entering: AsioInitState" << std::endl;
// Signals Mask for ASIO
boost::asio::signal_set signals(*(fsm.io_context), SIGINT, SIGTERM);
signals.async_wait([&](auto, auto) { fsm.io_context->stop(); });
}
template <class Event, class FSM>
void on_exit(Event const&, FSM&) { std::cout << "leaving: AsioInitState" << std::endl; }
};
struct RegisterServersState : public msm::front::state<>
{
template <class Event, class FSM>
void on_entry(Event const&, FSM& fsm) {
try {
std::cout << "entering: RegisterServersState" << std::endl;
co_spawn(*(fsm.io_context), boost::bind(&RegisterServersState::listener<Event, FSM>, this), detached);
std::cout << "entering end: RegisterServersState" << std::endl;
}
catch (const boost::system::system_error& e) {
std::cout << "> [Error - Server::start]: " << e.what() << std::endl;
}
}
template <class Event, class FSM>
void on_exit(Event const&, FSM&) { std::cout << "leaving: RegisterServersState" << std::endl; }
template <class Event, class FSM>
awaitable<void> listener() // all function is copy pasted from boost EchoServer example with added std::cout logs
{
auto executor = co_await this_coro::executor;
tcp::acceptor acceptor(executor, { tcp::v4(), 55555 });
for (;;)
{
std::cout << "listener - 0" << std::endl;
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
std::cout << "listener - 1" << std::endl;
co_spawn(executor, echo(std::move(socket)), detached);
}
}
awaitable<void> echo_once(tcp::socket& socket) // all function is copy pasted from boost EchoServer example
{
char data[128];
std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);
co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
}
awaitable<void> echo(tcp::socket socket) // all function is copy pasted from boost EchoServer example
{
try
{
for (;;)
{
// The asynchronous operations to echo a single chunk of data have been
// refactored into a separate function. When this function is called, the
// operations are still performed in the context of the current
// coroutine, and the behaviour is functionally equivalent.
co_await echo_once(socket);
}
}
catch (std::exception& e)
{
std::printf("echo Exception: %s\n", e.what());
}
}
};
// the initial state of the player SM. Must be defined
typedef AsioInitState initial_state;
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +------------------------+----------------------------+-----------------------------+---------------------+----------------------+
Row < AsioInitState , none , RegisterServersState >
> {};
boost::asio::io_context* io_context = { nullptr };
};
// Pick a back-end
typedef msm::back::state_machine<EchoServerSMFE> EchoServerSM;
main.cpp
#include <iostream>
#include "EchoServerMSM.h"
using namespace std;
int main()
{
EchoServerSM echoServer;
boost::asio::io_service ios(1);
echoServer.io_context = &ios;
echoServer.start();
ios.run();
cout << "Hello CMake." << endl;
return 0;
}
我得到的控制台输出是:
entering: AsioInitState
leaving: AsioInitState
entering: RegisterServersState
entering end: RegisterServersState
listener - 0
Hello CMake.
C:\Users\Andrea\source\repos\TGFLocalClient\out\build\x64-Debug (default)\TGFLocalClient\TGFLocalClient.exe (process 18280) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .
- 为什么
listener - 1
永远不会打印在控制台上?
- 如果队列中应该有其他协程,io_context 如何结束它的 运行?
- 听众
this_coro::executor
代表什么?用 *(fsm.io_context)
切换它会一样吗?
- 是否需要任何其他步骤才能使其作为回显服务器示例运行?
编辑:编译它的 cmake(win 或 mac):
cmake_minimum_required (VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
# FOR MAC
# set(CMAKE_CXX_FLAGS "-fcoroutines-ts")
#FOR WIN
# set(CMAKE_CXX_FLAGS "/await /EHsc")
# Boost section
# set(Boost_DEBUG "ON")
set(Boost_LIB_PREFIX "lib")
set(BOOST_ROOT "C:/Users/YourFolder/boost_1_75_0")
set(BOOST_LIBRARIES "date_time" "regex")
find_package(Boost COMPONENTS ${BOOST_LIBRARIES})
message(STATUS "${BOOST_ROOT}")
message(STATUS "${BOOST_FOUND}")
message(STATUS "${Boost_INCLUDE_DIRS}")
message(STATUS "${Boost_LIBRARY_DIRS}")
message(STATUS "${BOOST_LIBRARIES}")
message("${Boost_regex_FOUND}")
message("${Boost_regex_LIBRARY}")
include_directories("${Boost_INCLUDE_DIRS}")
# Add source to this project's executable.
add_executable (TGFLocalClient "main.cpp" "EchoServerMSM.h")
target_link_libraries(TGFLocalClient Boost::date_time Boost::regex)
void on_entry(Event const & /*unused*/, FSM & /*unused*/) {
std::cout << "entering: AsioInitState" << std::endl;
// Signals Mask for ASIO
boost::asio::signal_set signals(*(fsm.io_context), SIGINT,
SIGTERM);
signals.async_wait([&](auto, auto) { fsm.io_context->stop(); });
}
那里有问题。
signals
是局部变量。在退出 on_entry
函数时,它被破坏。
- 这意味着它将取消任何挂起的异步操作。
- 这反过来意味着用 ec =
boost::asio::error::operation_aborted
. 调用完成处理程序
- 完成处理程序不检查错误代码,只是在
io_service
上调用 stop
。
自从服务被强制停止后,没有发生任何其他事情。
我正在尝试构建一个结合文档中的两个提升示例的小示例。第一个是来自 MSM(state machines)库的示例:https://www.boost.org/doc/libs/1_75_0/libs/msm/doc/HTML/examples/AnonymousTutorialWithFunctors.cpp 第二个是来自 Asio 的 echo 服务器(带协程)示例:https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp 回显服务器示例 运行s 与我的 machine 正确。
状态 machine 有 2 个状态,AsioInitState
和 RegisterServersState
,在转换 table.
按照我的源代码: EchoServerMSM.h
#pragma once
// back-end
#include <boost/msm/back/state_machine.hpp>
//front-end
#include <boost/msm/front/state_machine_def.hpp>
// functors
#include <boost/msm/front/functor_row.hpp>
#include <boost/msm/front/euml/common.hpp>
// Asio
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace boost::msm::front;
using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::use_awaitable;
namespace this_coro = boost::asio::this_coro;
struct EchoServerSMFE : public msm::front::state_machine_def<EchoServerSMFE>
{
// The list of FSM states
struct AsioInitState : public msm::front::state<>
{
template <class Event, class FSM>
void on_entry(Event const&, FSM& fsm) {
std::cout << "entering: AsioInitState" << std::endl;
// Signals Mask for ASIO
boost::asio::signal_set signals(*(fsm.io_context), SIGINT, SIGTERM);
signals.async_wait([&](auto, auto) { fsm.io_context->stop(); });
}
template <class Event, class FSM>
void on_exit(Event const&, FSM&) { std::cout << "leaving: AsioInitState" << std::endl; }
};
struct RegisterServersState : public msm::front::state<>
{
template <class Event, class FSM>
void on_entry(Event const&, FSM& fsm) {
try {
std::cout << "entering: RegisterServersState" << std::endl;
co_spawn(*(fsm.io_context), boost::bind(&RegisterServersState::listener<Event, FSM>, this), detached);
std::cout << "entering end: RegisterServersState" << std::endl;
}
catch (const boost::system::system_error& e) {
std::cout << "> [Error - Server::start]: " << e.what() << std::endl;
}
}
template <class Event, class FSM>
void on_exit(Event const&, FSM&) { std::cout << "leaving: RegisterServersState" << std::endl; }
template <class Event, class FSM>
awaitable<void> listener() // all function is copy pasted from boost EchoServer example with added std::cout logs
{
auto executor = co_await this_coro::executor;
tcp::acceptor acceptor(executor, { tcp::v4(), 55555 });
for (;;)
{
std::cout << "listener - 0" << std::endl;
tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
std::cout << "listener - 1" << std::endl;
co_spawn(executor, echo(std::move(socket)), detached);
}
}
awaitable<void> echo_once(tcp::socket& socket) // all function is copy pasted from boost EchoServer example
{
char data[128];
std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);
co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
}
awaitable<void> echo(tcp::socket socket) // all function is copy pasted from boost EchoServer example
{
try
{
for (;;)
{
// The asynchronous operations to echo a single chunk of data have been
// refactored into a separate function. When this function is called, the
// operations are still performed in the context of the current
// coroutine, and the behaviour is functionally equivalent.
co_await echo_once(socket);
}
}
catch (std::exception& e)
{
std::printf("echo Exception: %s\n", e.what());
}
}
};
// the initial state of the player SM. Must be defined
typedef AsioInitState initial_state;
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +------------------------+----------------------------+-----------------------------+---------------------+----------------------+
Row < AsioInitState , none , RegisterServersState >
> {};
boost::asio::io_context* io_context = { nullptr };
};
// Pick a back-end
typedef msm::back::state_machine<EchoServerSMFE> EchoServerSM;
main.cpp
#include <iostream>
#include "EchoServerMSM.h"
using namespace std;
int main()
{
EchoServerSM echoServer;
boost::asio::io_service ios(1);
echoServer.io_context = &ios;
echoServer.start();
ios.run();
cout << "Hello CMake." << endl;
return 0;
}
我得到的控制台输出是:
entering: AsioInitState
leaving: AsioInitState
entering: RegisterServersState
entering end: RegisterServersState
listener - 0
Hello CMake.
C:\Users\Andrea\source\repos\TGFLocalClient\out\build\x64-Debug (default)\TGFLocalClient\TGFLocalClient.exe (process 18280) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .
- 为什么
listener - 1
永远不会打印在控制台上? - 如果队列中应该有其他协程,io_context 如何结束它的 运行?
- 听众
this_coro::executor
代表什么?用*(fsm.io_context)
切换它会一样吗? - 是否需要任何其他步骤才能使其作为回显服务器示例运行?
编辑:编译它的 cmake(win 或 mac):
cmake_minimum_required (VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
# FOR MAC
# set(CMAKE_CXX_FLAGS "-fcoroutines-ts")
#FOR WIN
# set(CMAKE_CXX_FLAGS "/await /EHsc")
# Boost section
# set(Boost_DEBUG "ON")
set(Boost_LIB_PREFIX "lib")
set(BOOST_ROOT "C:/Users/YourFolder/boost_1_75_0")
set(BOOST_LIBRARIES "date_time" "regex")
find_package(Boost COMPONENTS ${BOOST_LIBRARIES})
message(STATUS "${BOOST_ROOT}")
message(STATUS "${BOOST_FOUND}")
message(STATUS "${Boost_INCLUDE_DIRS}")
message(STATUS "${Boost_LIBRARY_DIRS}")
message(STATUS "${BOOST_LIBRARIES}")
message("${Boost_regex_FOUND}")
message("${Boost_regex_LIBRARY}")
include_directories("${Boost_INCLUDE_DIRS}")
# Add source to this project's executable.
add_executable (TGFLocalClient "main.cpp" "EchoServerMSM.h")
target_link_libraries(TGFLocalClient Boost::date_time Boost::regex)
void on_entry(Event const & /*unused*/, FSM & /*unused*/) {
std::cout << "entering: AsioInitState" << std::endl;
// Signals Mask for ASIO
boost::asio::signal_set signals(*(fsm.io_context), SIGINT,
SIGTERM);
signals.async_wait([&](auto, auto) { fsm.io_context->stop(); });
}
那里有问题。
signals
是局部变量。在退出on_entry
函数时,它被破坏。- 这意味着它将取消任何挂起的异步操作。
- 这反过来意味着用 ec =
boost::asio::error::operation_aborted
. 调用完成处理程序
- 完成处理程序不检查错误代码,只是在
io_service
上调用stop
。
自从服务被强制停止后,没有发生任何其他事情。