[英]C++: Boost::asio: Wait for asynchronous handler in the same function
[英]How to wait for a function to return with Boost:::Asio?
我是使用 Boost::Asio 庫的新手,並且無法獲得我想要的行為。 我正在嘗試為自定義硬件解決方案實施一些網絡通信。 我們使用的通信協議棧嚴重依賴於 Boost::Asio 異步方法,我不相信它是完全線程安全的。
我已成功實現發送,但在嘗試設置等待接收時遇到問題。 我發現的大多數boost::asio 示例都依賴於套接字行為來使用socket_.async_read_some()
或其他類似函數來實現異步等待。 但是這對我們不起作用,因為我們的硬件解決方案需要直接調用驅動程序 function 而不是使用 sockets。
該應用程序使用傳遞給boost::asio::generic::raw_protocol::socket
以及其他類的io_service
。
這是來自協議棧的示例代碼。 在 RawSocketLink 的構造函數中調用do_receive()
。
void RawSocketLink::do_receive()
{
namespace sph = std::placeholders;
socket_.async_receive_from(
boost::asio::buffer(receive_buffer_), receive_endpoint_,
std::bind(&RawSocketLink::on_read, this, sph::_1, sph::_2));
}
void RawSocketLink::on_read(const boost::system::error_code& ec, std::size_t read_bytes)
{
if (!ec) {
// Do something with received data...
do_receive();
}
}
在實現堆棧之前,我們一直在使用線程庫為發送和接收創建單獨的線程。 接收方法如下所示。 大多數情況下,它依賴於從硬件驅動程序調用receive_data()
function 並等待它返回。 這是一個阻塞調用,但需要返回數據。
void NetworkAdapter::Receive() {
uint8_t temp_rx_buffer[2048];
rc_t rc;
socket_t *socket_ptr;
receive_params_t rx_params;
size_t rx_buffer_size;
char str[100];
socket_ptr = network_if[0];
while (1) {
rx_buffer_size = sizeof(temp_rx_buffer);
// Wait until receive_data returns then process
rc = receive_data(socket_ptr,
temp_rx_buffer,
&rx_buffer_size,
&rx_params,
WAIT_FOREVER);
if (rc_error(rc)) {
(void)fprintf(stderr, "Receive failed");
continue;
}
// Do something with received packet ....
}
return;
}
請注意,此代碼中的socket_t
指針與 Boost::Asio 的 TCP/UDP 套接字不同。
這是我當前的代碼,我需要幫助。 我不確定如何使用 boost::asio 方法等待 receive_data 返回。 我們正在嘗試復制socket.async_read_from()
的行為。 NetworkAdapter 可以訪問io_service
。
void NetworkAdapter::do_receive() {
rc_t rc;
socket_t *socket_ptr;
receive_params_t rx_params;
size_t rx_buffer_size;
socket_ptr = network_if[0];
rx_buffer_size = receive_buffer_.size();
// What do I put here to await for this to return asynchronously?
rc = receive_data(socket_ptr, receive_buffer_.data(), &rx_buffer_size, &rx_params, ATLK_WAIT_FOREVER);
on_read(rc, rx_buffer_size, rx_params);
}
void NetworkAdapter::on_read(const rc_t &rc, std::size_t read_bytes, const receive_params_t &rx_params) {
if (!rc) {
// Do something with received data...
} else {
LOG(ERROR) << "Packet receieve failure";
}
do_receive();
}
如何使用 boost::asio async/await 函數等待 function 返回? 特別是我想復制socket.async_receive_from()
的行為,但使用 function 而不是套接字。
*由於數據保護要求,某些 function 名稱和類型已更改。
N4045 異步操作庫基礎,修訂版 2
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4045.pdf
在第 24 頁上,有一個關於如何根據基於回調的操作系統 API 實現 asio 異步 API 的示例。
// the async version of your operation, implementing all kinds of async paradigm in terms of callback async paradigm
template <class CompletionToken>
auto async_my_operation(/* any parameters needed by the sync version of your operation */, CompletionToken&& token)
{
// if CompletionToken is a callback function object, async_my_operation returns void, the callback's signature should be void(/* return type of the sync version of your operation, */error_code)
// if CompletionToken is boost::asio::use_future, async_my_operation returns future</* return type of the sync version of your operation */>
// if CompletionToken is ..., ...
// you are not inventing new async paradigms so you don't have to specialize async_completion or handler_type, you should focus on implement the os_api below
async_completion<CompletionToken, void(/* return type of the sync version of your operation, */error_code)/* signature of callback in the callback case */> completion(token);
typedef handler_type_t<CompletionToken, void(error_code)> Handler;
unique_ptr<wait_op<Handler>> op(new wait_op<Handler>(move(completion.handler))); // async_my_operation initates your async operation and exits, so you have to store completion.handler on the heap, the completion.handler will be invoked later on a thread pool (e.g. threads blocked in IOCP if you are using os api, threads in io_context::run() if you are using asio (sockets accept an io_context during construction, so they know to use which io_context to run completion.handler))
// most os api accepts a void* and a void(*)(result_t, void*) as its C callback function, this is type erasure: the void* points to (some struct that at least contains) the C++ callback function object (can be any type you want), the void(*)(result_t, void*) points to a C callback function to cast the void* to a pointer to C++ callback function object and call it
os_api(/* arguments, at least including:*/ op.get(), &wait_callback<Handler>);
return completion.result.get();
}
// store the handler on the heap
template <class Handler>
struct wait_op {
Handler handler_;
explicit wait_op(Handler handler) : handler_(move(handler)) {}
};
// os post a message into your process's message queue, you have several threads blocking in a os api (such as IOCP) or asio api (such as io_context::run()) that continuously takes message out from the queue and then call the C callback function, the C callback function calls your C++ callback function
template <class Handler>
void wait_callback(result_t result, void* param)
{
unique_ptr<wait_op<Handler>> op(static_cast<wait_op<Handler>*>(param));
op‐>handler_(/* turn raw result into C++ classes before passing it to C++ code */, error_code{});
}
//trivial implementation, you should consult the socket object to get the io_context it uses
void os_api(/* arguments needed by your operation */, void* p_callback_data, void(*p_callback_function)(result_t, void*))
{
std::thread([](){
get the result, blocks
the_io_context_of_the_socket_object.post([](){ (*p_callback_function)(result, p_callback_data); });
}).detach();
}
boost.asio 已經從async_completion
和handler_type
更改為async_result
,所以上面的代碼已經過時了。
異步操作要求 - 1.75.0 https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/asynchronous_operations.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.