我正在尝试使用执行以下操作的asio编写客户端:
我发现的问题是操作似乎没有像我期望的那样按顺序执行。这是代码:
std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname,
int port,
std::string const & name) {
using namespace std::literals;
boost::asio::spawn
(strand_,
[this, name, port, hostname](boost::asio::yield_context yield) mutable {
i_->playerLog->trace() << name << " connecting to " << hostname << ':'
<< port;
Guinyote::Utils::connectWith
(*this, std::move(hostname), port,
std::move(name), yield);
i_->playerLog->info() << "Connected to server.";
});
runthread_ = std::thread([&] {
try {
i_->playerLog->info() << "Starting...";
this->service_.run();
}
catch (std::exception & e) {
std::cout << e.what() << std::endl;
}
});
return this->asyncReceiveMessage(); //This function spawns another coroutine through the same strand_ object.
}
函数this->asyncReceiveMessage()
应该会在连接后收到服务器发回的消息:
std::future<NetMessage> Cliente::asyncReceiveMessage() {
namespace ba = boost::asio;
std::promise<NetMessage> prom;
std::future<NetMessage> message = prom.get_future();
ba::spawn
(strand_,
[this, p = std::move(prom)](boost::asio::yield_context yield) mutable {
i_->playerLog->trace("waiting to read message from server socket...");
boost::system::error_code ec{};
boost::int64_t messageSize{};
ba::async_read(
socket_,
ba::buffer(&messageSize, sizeof(boost::int64_t)),
yield);
i_->playerLog->trace() << "Client: Received message of "
<< messageSize << " bytes. Reading message...";
std::vector<char> serverMessageData(messageSize);
ba::async_read
(socket_,
ba::buffer(serverMessageData),
yield);
using namespace boost::iostreams;
basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size());
stream<basic_array_source<char>> stream(input_source);
boost::archive::binary_iarchive archive(stream);
Utils::MensajeRed msg;
archive >> msg;
i_->playerLog->trace() << "NetMessage correctly read.";
p.set_value(std::move(msg));
});
return message;
}
在我的日志文件中,我在客户端获得以下内容:
[clientLog] [info] Client: Starting...
[clientLog] [trace] User1234 connecting to localhost:10004
[clientLog] [trace] waiting to read message from server socket...
但我希望该日志的第三行会在[clientLog] [info] Connected to server.
之后出现所以我的期望如下:
[clientLog] [info] Client: Starting...
[clientLog] [trace] User1234 connecting to localhost:10004
[clientLog] [info] Connected to server.
[clientLog] [trace] waiting to read message from server socket...
即&#34;连接到服务器&#34;应始终之前等待从服务器套接字读取消息...&#34;。
有谁知道发生了什么事?我以为strand_
会保证执行顺序,但似乎我可能误解了一些东西。获得我想要的效果的正确解决方案是什么?
答案 0 :(得分:1)
所以这里的问题是任务将按顺序启动,但链中的后续boost::asio::spawn
spawn调用并不意味着第一个任务将在第二个任务之前完成。
第一个任务在第二个任务之前启动,只会发生
。为了保持顺序,我只创建了一个在asyncConnectTo
中的spawn中调用的协程,而不是产生两个不同的协同程序。通过这种方式,我确保第一个协程在第二个协程之前完成:
NetMessage Cliente::asyncReceiveMessageCoro(boost::asio::yield_context yield) {
namespace ba = boost::asio;
i_->playerLog->trace("waiting to read message from server socket...");
boost::system::error_code ec{};
boost::int64_t messageSize{};
ba::async_read(
socket_,
ba::buffer(&messageSize, sizeof(boost::int64_t)),
yield);
i_->playerLog->trace() << "Client: Received message of "
<< messageSize << " bytes. Reading message...";
std::vector<char> serverMessageData(messageSize);
ba::async_read
(socket_,
ba::buffer(serverMessageData),
yield);
using namespace boost::iostreams;
basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size());
stream<basic_array_source<char>> stream(input_source);
boost::archive::binary_iarchive archive(stream);
Utils::MensajeRed msg;
archive >> msg;
i_->playerLog->trace() << "NetMessage correctly read.";
return msg;
}
这个协程可以在最后链接:
std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname,
int port,
std::string const & name) {
using namespace std::literals;
std::promise<NetMessage> msgProm;
auto msg = msgProm.get_future();
boost::asio::spawn
(strand_,
[this, name, port, hostname, p = std::move(msgProm)](boost::asio::yield_context yield) mutable {
i_->playerLog->trace() << name << " connecting to " << hostname << ':'
<< port;
Guinyote::Utils::connectWith
(*this, std::move(hostname), port,
std::move(name), yield);
i_->playerLog->info() << "Connected to server.";
p.set_value(this->asyncReceiveCoro(yield));
});
runthread_ = std::thread([&] {
try {
i_->playerLog->info() << "Starting...";
this->service_.run();
}
catch (std::exception & e) {
std::cout << e.what() << std::endl;
}
});
return msg;
}
我的旧asyncReceiveMessage
只是spawn
+调用asyncReceiveMessageCoro
的组合。