【问题标题】:Boost asio async_read_until followed by async_read提升 asio async_read_until 后跟 async_read
【发布时间】:2018-04-05 04:25:33
【问题描述】:

我使用了 boost 中的异步 tcp 服务器示例,它与我的应用程序正在执行的操作很接近。下面的代码示例是一个完整的工作示例。

首先我开始异步读取操作,直到分隔符字符。在这种情况下,它是 http 标头完整序列。该请求包含一些有效载荷,即“hello world”(11 个字节)。对于一个简化的示例,我在这里使用 lambda 处理程序。第一个处理程序被称为长度为 148 的标题,它是包含四个字节的定界符序列的标头。

缓冲区的大小为我提供了 159,这是包括有效负载在内的整个请求。到目前为止,一切都按预期工作。为了接收有效负载,我调用了另一个异步读取操作,但从未调用过处理程序。我尝试的第一个是读取 11 个字节,但没有成功,所以我尝试只读取两个字节来检查它是否正常工作,但不是。

streambuf 已经包含了所有数据,但是为什么没有调用异步读取处理程序。是不是应该立即调用,因为数据在缓冲区内,或者是否存在滥用 api 的情况?

编辑1:

我最终检查了计算要读取的字节与缓冲区内的字节。当不需要“真正的”读取操作时,我使用 io_server::post 为处理程序添加包装器。这似乎是为此目的的最佳选择。

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class session
  : public std::enable_shared_from_this<session>
{
public:
  session(tcp::socket socket)
    : socket_(std::move(socket))
  {
  }

    boost::asio::streambuf buf;

  void start()
  {
    do_read();
  }

private:
  void do_read()
  {
    auto self(shared_from_this());

    boost::asio::async_read_until(socket_,buf, "\r\n\r\n", [this, self](boost::system::error_code ec, std::size_t length){
        std::istream in(&buf);
        std::cout << length << std::endl;
        std::cout << buf.size() << std::endl;
        in.ignore(length);
        boost::asio::async_read(socket_, buf, boost::asio::transfer_exactly(2), [this, self](boost::system::error_code ec, std::size_t length){
            std::istream in(&buf);
            std::cout << length << std::endl;
        });
    });
  }

  void do_write(std::size_t length)
  {
    auto self(shared_from_this());
    boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
        [this, self](boost::system::error_code ec, std::size_t /*length*/)
        {
          if (!ec)
          {
            do_read();
          }
        });
  }

  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
      socket_(io_service)
  {
    do_accept();
  }

private:
  void do_accept()
  {
    acceptor_.async_accept(socket_,
        [this](boost::system::error_code ec)
        {
          if (!ec)
          {
            std::make_shared<session>(std::move(socket_))->start();
          }

          do_accept();
        });
  }

  tcp::acceptor acceptor_;
  tcp::socket socket_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    server s(io_service, std::atoi(argv[1]));

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

【问题讨论】:

    标签: c++ asynchronous boost tcp boost-asio


    【解决方案1】:

    第二个 async_read 只读取 2 个字节。您明确地说您要“准确传输 2 个字节”。

    您正在查看的是您不想/想要/读取 2 个字节,因为您已经这样做了。你可以调整如下:

    size_t to_transfer = 2 - std::min(2ul, buf.size());
    boost::asio::async_read(socket_, buf, boost::asio::transfer_exactly(to_transfer),
    

    在一个稍微相关的注释中,它可能似乎不会调用回调,因为您没有显式刷新 std::cout。所以加了

    std::cout << std::unitbuf;
    

    main 可以消除这种潜在的混淆来源。

    演示

    Live On Coliru

    这个示例在命名length(不是长度,而是bytes_transferred)和为buf.size() 转储更多值时更加小心,因此您会看到真正发生了什么。 p>

    #include <boost/asio.hpp>
    #include <cstdlib>
    #include <iostream>
    #include <memory>
    #include <utility>
    
    using boost::asio::ip::tcp;
    
    class session : public std::enable_shared_from_this<session> {
      public:
        session(tcp::socket&& s) : socket_(std::move(s)) {}
    
        boost::asio::streambuf buf;
    
        void start() { do_read(); }
    
      private:
        void do_read() {
            auto self(shared_from_this());
    
            boost::asio::async_read_until(
                socket_, buf, "\r\n\r\n", [this, self](boost::system::error_code ec, std::size_t transferred) {
                    std::cout << "async_read_until -> " << ec.message() << "\n";
                    {
                        std::cout << "transferred: " << transferred << std::endl;
                        std::cout << "buffer: " << buf.size() << std::endl;
                        {
                            std::istream in(&buf);
                            in.ignore(transferred);
                        }
                        std::cout << "remaining buffer: " << buf.size() << std::endl;
                    }
    
                    size_t to_transfer = 2 - std::min(2ul, buf.size());
                    boost::asio::async_read(socket_, buf, boost::asio::transfer_exactly(to_transfer),
                        [this, self](boost::system::error_code ec, std::size_t transferred) {
                            std::cout << "async_read -> " << ec.message() << "\n";
                            std::cout << "transferred: " << transferred << std::endl;
                            std::cout << "buffer: " << buf.size() << std::endl;
                            {
                                std::istream in(&buf);
                                char a, b;
                                if (in >> a >> b) {
                                    std::cout << "Payload: '" << a << b << "'\n";
                                }
                            }
                            std::cout << "remaining buffer: " << buf.size() << std::endl;
                        });
                });
        }
    
        friend class server;
        tcp::socket socket_;
    };
    
    class server {
      public:
        server(boost::asio::io_service &io_service, short port) : acceptor_(io_service, tcp::endpoint({}, port)), socket_(io_service) {
            do_accept();
        }
    
      private:
        void do_accept() {
            acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
                std::cout << "async_accept -> " << ec.message() << "\n";
                if (!ec) {
                    std::make_shared<session>(std::move(socket_))->start();
                    //do_accept();
                }
            });
        }
    
        tcp::acceptor acceptor_;
        tcp::socket socket_;
    };
    
    int main() {
        std::cout << std::unitbuf;
        try {
            boost::asio::io_service io_service;
    
            server s(io_service, 6767);
    
            io_service.run();
        } catch (std::exception &e) {
            std::cerr << "Exception: " << e.what() << "\n";
        }
    }
    

    例如像“客户”这样的

    echo -e 'hello\r\n\r\nmore to come' | netcat localhost 6767
    

    输出看起来像

    async_accept -> Success
    async_read_until -> Success
    transferred: 9
    buffer: 22
    remaining buffer: 13
    async_read -> Success
    transferred: 0
    buffer: 13
    Payload: 'mo'
    remaining buffer: 11
    

    【讨论】:

      猜你喜欢
      • 2018-09-22
      • 1970-01-01
      • 2017-07-20
      • 2018-04-01
      • 2011-03-04
      • 1970-01-01
      • 2013-12-21
      • 2016-04-08
      • 1970-01-01
      相关资源
      最近更新 更多