当streambuf由以前的async_read填充时,boost::asio::async_read::streamb

boost::asio::async_read into boost::asio::streambuf blocks when streambuf is populated by previous async_read

本文关键字:read async boost streambuf asio streamb 填充      更新时间:2023-10-16

我搜索过其他帖子,但没有找到任何相关的内容。现在,我有一个由标头和正文组成的协议。协议类似于:z2491009802123456789bcdef其中Z24,是收割台。Z是消息类型,24是要读取的剩余字节。剩余的字节是可变的,所以我一直读到找到第一个","。

void handle_handshake(const boost::system::error_code& error)
{
    if (!error)
    {
        boost::asio::async_read_until(
            socket_,
            inputStreamBuffer_,
            ',',
            boost::bind(
                &session::doReadHeader, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred)
        );
    }
    else
    {
        delete this;
    }
}
void doReadHeader(
    const boost::system::error_code& error,
    size_t bytes_transferred)
{
    if (!error)
    {
        istream is(&inputStreamBuffer_);
        vector<char> v(bytes_transferred);
        is.read(&(v[0]),bytes_transferred);
        request_.append(v.begin(),v.end());
        cout << "request_=#" << request_ << "#" << endl;
        int nBytes=string_to_llint(request_.substr(1,request_.size()-2));
        cout << "nBytes=" << nBytes << endl;
        cout << "size=" << inputStreamBuffer_.size() << endl;
        boost::asio::async_read(
            socket_,
            inputStreamBuffer_,
            boost::asio::transfer_at_least(nBytes),
            boost::bind(
                &session::doReadBody, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred)
        );
    }
    else
    {
        delete this;
    }
}
void doReadBody(
    const boost::system::error_code& error,
    size_t bytes_transferred)
{
    if (!error)
    {
        istream is(&inputStreamBuffer_);
        vector<char> v(bytes_transferred);
        is.read(&(v[0]),bytes_transferred);
        request_.append(v.begin(),v.end());
        string response=cardIssueProcessor_.process(request_);
        cout << "request=#" << request_ << "#" << endl;
        cout << "response=#" << response << "#" << endl;
        request_.clear();
        boost::asio::async_write(
            socket_,
            boost::asio::buffer(response, response.size()),
            boost::bind(
                &session::doWriteResponse, this,
                boost::asio::placeholders::error)
        );
    }
    else
    {
        delete this;
    }
}

现在,读取标题。但是读取页脚会阻塞。显然,整个消息都在标头调用中读取。当我用boost::asio::transfer_at_least(nBytes)执行第二个async_read()时,nBytes已经在inputStreamBuffer_中,但我认为调用没有检查这一点?

这是输出的转储:

request_=#Z24#n字节=24大小=24

问题出在哪里,或者我该如何解决。我是一个新手,所以感谢所有的帮助。非常感谢。

编辑:我试着检查缓冲区的填充情况,如果上一次调用已经读取了正文,就不要对其进行async_read()调用。这是可行的,但这是正确的解决方案吗?

void doReadHeader(
    const boost::system::error_code& error,
    size_t bytes_transferred)
{
    if (!error)
    {
        istream is(&inputStreamBuffer_);
        vector<char> v(bytes_transferred);
        is.read(&(v[0]),bytes_transferred);
        request_.assign(v.begin(),v.end());
        cout << "request_=#" << request_ << "#" << endl;
        int nBytes=string_to_llint(request_.substr(1,request_.size()-2));
        cout << "nBytes=" << nBytes << endl;
        cout << "size=" << inputStreamBuffer_.size() << endl;
        size_t toReadBytes=nBytes-inputStreamBuffer_.size();
        if (toReadBytes>0)
        {
            boost::asio::async_read(
                socket_,
                inputStreamBuffer_,
                boost::asio::transfer_at_least(toReadBytes),
                boost::bind(
                    &session::doReadBody, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred)
            );
        }
        else
        {
            doReadBody(error,nBytes);
        }
    }
    else
    {
        delete this;
    }
}

Boost ASIO文档指出async_read_until调用可能会将超出分隔符的数据读取到缓冲区中(请参阅备注部分)。也就是说,考虑到您的输入,检查缓冲区是否有更多数据的解决方案是一个很好的解决方案。

正如我在上面的评论中提到的,如果您的需求允许您这样做,那么使用剩余字节的整数值而不是字符串可能会让您的生活更轻松,代码也更干净,不容易出错。

async_read_until可以读取超过分隔符的字节

备注

async_read_until成功后操作,streambuf可能包含分隔符之外的其他数据。申请通常会离开streambuf中的数据后续async_read_until操作检查。