boost::asio::async_read_until 从套接字拉取太多内容

问题描述 投票:0回答:1

我正在尝试接收以下格式的消息

<size> <data>

地点:

  • <size>
    是一个整数,表示
    <data>
  • 的大小
  • <size>
    <data>
    由空格分隔。

示例消息是:

19 {"command":"start"}

这是我必须解析的类:

class Command : {
public:
    Command(boost::asio::ip::tcp::socket& socket) : _socket(socket) {}
    std::future<boost::property_tree::ptree> Receive();

private:
    void OnSize(const std::error_code&, size_t);
    void OnData(const std::error_code&, size_t);

    boost::asio::ip::tcp::socket&              _socket;
    boost::asio::streambuf                     _buffer;
    std::promise<boost::property_tree::ptree>  _promise;
}

首先我需要 . 我调用

async_read_until(...,' ',...)
将数据拉至(并包括)空白区域。 这一切都存储在
boost::asio::streambuf
:

std::future<boost::property_tree::ptree> 
Command::Receive() {
    boost::asio::async_read_until(_socket, _buffer, ' ', 
        std::bind(&Command::OnSize, this, _1, _2)
    );
    return _promise.get_future();
}

当这是阻塞时,我然后使用

netcat
发送示例消息:

$ netcat 127.0.0.1 14652
19 {"command":"start"}

如果我使用wireshark进行检查,整个消息将在单个TCP帧中发送。

处理程序

OnSize()
按预期调用,并使用
bytes_received = 3
。这是有道理的,因为 2 个字节表示
<size>
加上 1 个字节表示空白。 然后我希望我只需要
transfer_exactly(data_size)
阅读其余部分。

void Command::OnSize(const std::error_code& ec, std::size_t bytes_received) {

    std::size_t data_size;
    std::istream is(&_buffer);
    is >> data_size;  // automatically consumes the digits from the buffer
        
    // consume the whitespace
    _buffer.consume(1);
    
    boost::asio::async_read(
        _socket, _buffer, boost::asio::transfer_exactly(data_size),
        std::bind(&Command::OnData, this, _1, _2)
    );
}

接下来我打电话给

async_read(..., transfer_exactly(data_size), ...)
,希望
OnData()
bytes_received == data_size
打电话。 然而,我发现它会阻塞,直到我通过垃圾邮件发送随机数据来触发另一个至少
data_size
的 TCP 帧,这是一个问题,因为我不期望更多数据。

void Command::OnData(const std::error_code& ec, std::size_t bytes_received) {
    std::istream is(&m_buffer);
    boost::property_tree::ptree pt;
    boost::property_tree::json_parser::read_json(is, pt);

    _promise.set_value(pt);
}

有趣的是,

_buffer.size() == 23
位于
OnSize()
的开头,而
_buffer.size() == 20
位于
async_read
之前。 这意味着其余数据实际上已经写入缓冲区。 我该怎么办?

c++ boost-asio
1个回答
0
投票

这是一个解决方案,但我不知道这是否是正确的方法。 这个想法是检查缓冲区中有多少数据,并且仅

asyc_read()
缓冲区中已有的数据与您期望的数据之间的差异。

void Command::OnSize(const std::error_code& ec, std::size_t bytes_received) {

    std::size_t data_size;
    std::istream is(&_buffer);
    is >> data_size;

    _buffer.consume(1);

    auto unread_bytes = _buffer.size();
    if (unread_bytes >= data_size) {
        OnData(ec, data_size);
    }
    else {
        auto remaining = data_size - unread_bytes;
        boost::asio::async_read(
            _socket, _buffer, boost::asio::transfer_exactly(remaining),
            std::bind(&Command::OnData, this, _1, _2)
        );
    }
}

我无法用大数据集对此进行测试,但

unread_bytes >= data_size
条件似乎效果很好。 在我的
lo
设备上,即使对于大型数据集(5000字节),这似乎也有效,尽管我通过wireshark确认这仍然是在单个TCP帧上发送的。

© www.soinside.com 2019 - 2024. All rights reserved.