简体   繁体   English

Boost.Asio async_read 从套接字中读取字符串

[英]Boost.Asio async_read a string from a socket

I'm trying to write a function async_read_string_n to asynchronously read a string of exactly n bytes from a socket with Boost.Asio 1.78 (and GCC 11.2).我正在尝试编写一个 function async_read_string_n以从具有 Boost.Asio 1.78(和 GCC 11.2)的套接字中异步读取正好n个字节的字符串。

This is how I want to use the function async_read_string_n :这就是我想使用 function async_read_string_n的方式:

void run() {
  co_spawn (io_context_, [&]() -> awaitable<void> {
    auto executor = io_context_.get_executor();
    tcp::acceptor acceptor(executor, listen_endpoint_);

    auto [ec, socket] = co_await acceptor.async_accept(as_tuple(use_awaitable));
    co_spawn(executor, [&]() -> awaitable<void> {

      auto [ec, header] = co_await async_read_string_n(socket, 6, as_tuple(use_awaitable));
      std::cerr << "received string " << header << "\n";
      co_return;
    }
    , detached);
    co_return;
  }
  , detached);
}
  

Here is my attempt to write async_read_string_n , following the advice in这是我按照中的建议编写async_read_string_n的尝试

(I don't care about memory copying. This isn't supposed to be fast; it's supposed to have a nice API.) (我不关心 memory 复制。这不应该很快;它应该有一个不错的 API。)

template<class CompletionToken> auto async_read_string_n(tcp::socket& socket, int n, CompletionToken&& token) {
  async_completion<CompletionToken, void(boost::system::error_code, std::string)> init(token);
  asio::streambuf b;
  asio::streambuf::mutable_buffers_type bufs = b.prepare(n);
  auto [ec, bytes_transferred] = co_await asio::async_read(socket, bufs, asio::transfer_exactly(n), as_tuple(use_awaitable));
  b.commit(n);
  std::istream is(&b);
  std::string s;
  is >> s;
  b.consume(n);
  init.completion_handler(ec, s);
  return init.result.get();
}

Edit编辑

(I had a syntax error and I fixed it.) Here is the compiler error in async_read_string_n which I'm stuck on: (我有一个语法错误,我修复了它。)这是我坚持的async_read_string_n中的编译器错误:

GCC error: GCC 错误:

error: 'co_await' cannot be used in a function with a deduced return type

How can I write the function async_read_string_n ?如何编写 function async_read_string_n

You don't have to use streambuf .您不必使用streambuf Regardless, using the >> extraction will not reliably extract the string (whitespace stops the input).无论如何,使用>>提取不会可靠地提取字符串(空格会停止输入)。

The bigger problem is that you have to choose whether you want to use更大的问题是你必须选择是否要使用

  • co_await (which requires another kind of signature as your second link correctly shows) co_await (需要另一种签名,因为您的第二个链接正确显示)
  • or the async result protocol, which implies that the caller will decide what mechanism to use (a callback, future, group, awaitable etc).异步结果协议,这意味着调用者将决定使用什么机制(回调、未来、组、等待等)。

So either make it:所以要么做到:

Using Async Result Protocol:使用异步结果协议:

#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/experimental/as_tuple.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <iostream>
#include <iomanip>
namespace net = boost::asio;
using net::ip::tcp;
using boost::system::error_code;

template <typename CompletionToken>
auto async_read_string_n(tcp::socket& socket, int n, CompletionToken&& token)
{
    struct Op {
        net::async_completion<CompletionToken, void(error_code, std::string)>
            init;
        std::string buf;
        Op(CompletionToken token) : init(token) {}
    };
    auto op = std::make_shared<Op>(token);

    net::async_read(socket, net::dynamic_buffer(op->buf),
                    net::transfer_exactly(n), [op](error_code ec, size_t n) {
                        op->init.completion_handler(ec, std::move(op->buf));
                    });
    return op->init.result.get();
}

int main() {
    net::io_context ioc;
    tcp::socket s(ioc);
    s.connect({{}, 8989});

    async_read_string_n(s, 10, [](error_code ec, std::string s) {
        std::cout << "Read " << ec.message() << ": " << std::quoted(s)
                  << std::endl;
    });

    ioc.run();
}

Prints印刷

在此处输入图像描述

OR Use co_await使用 co_await

Analogous to the sample here :类似于此处的示例:

boost::asio::awaitable<void> echo(tcp::socket socket)
{
  char data[1024];
  for (;;)
  {
    auto [ec, n] = co_await socket.async_read_some(boost::asio::buffer(data),
        boost::asio::experimental::as_tuple(boost::asio::use_awaitable));
    if (!ec)
    {
      // success
    }

    // ...
  }
}

Thank you @sehe for your answer, which gave me the information I needed to write async_read_string_n which works with co_await :感谢@sehe 的回答,它为我提供了编写与async_read_string_n一起使用的co_await所需的信息:

asio::awaitable<std::tuple<boost::system::error_code, std::string>> async_read_string_n(tcp::socket& socket, int n) {
  std::string buf;
  auto [ec, bytes_transferred] = co_await asio::async_read(socket, asio::dynamic_buffer(buf), asio::transfer_exactly(n), as_tuple(use_awaitable));
  co_return make_tuple(ec, buf);
}

Use it like this:像这样使用它:

auto [ec, string6] = co_await async_read_string_n(socket, 6);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM