当 streambuf 由先前的 async_read 填充时, boost::asio::async_read 进入 boost::asio::streambuf 块

Posted

技术标签:

【中文标题】当 streambuf 由先前的 async_read 填充时, boost::asio::async_read 进入 boost::asio::streambuf 块【英文标题】:boost::asio::async_read into boost::asio::streambuf blocks when streambuf is populated by previous async_read 【发布时间】:2011-06-16 11:10:43 【问题描述】:

我搜索了其他帖子,但没有找到任何相关内容。 现在,我有一个由标头和正文组成的协议。 协议如下: Z24,91009802,123456789ABCDEF 其中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_ 中,但我认为调用没有检查这个?

这是输出中的转储:

请求_=#Z24,# nBytes=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;
    

【问题讨论】:

在对主体执行 async_read() 之前,我尝试检查缓冲区填充度。我有点工作。这是正确的解决方案吗? 我不确定您的要求是否允许您这样做,但通常更容易使用已定义字节大小的整数值来表示要读取的剩余字节,而不是可变长度的字符串。 【参考方案1】:

The Boost ASIO documentation 表示 async_read_until 调用可能会将数据读入超出分隔符的缓冲区(请参阅备注部分)。话虽如此,根据您的输入,您检查缓冲区是否有更多数据的解决方案是一个很好的解决方案。

正如我在上面的评论中提到的,如果您的要求允许您这样做,则对剩余字节使用整数值而不是字符串可能会让您的生活更轻松,代码更简洁,错误更少易发。

【讨论】:

首先,感谢您的回复。现在说得通了。但我所期望的是,async_read_until() 将返回它实际从套接字读取的字节数的 bytes_transferred,而不是返回直到分隔字符的字节。 无论如何,我制定的解决方案似乎正在工作,但 async_read() 调用需要一些更改:boost::asio::async_read(socket_, inputStreamBuffer_, boost::asio::transfer_at_least(toReadBytes), boost::bind(&amp;session::doReadBody, this, boost::asio::placeholders::error, nBytes) ); boost::asio::placeholders::bytes_transfered 实际上表示来自的新字节套接字,并且不包括先前使用 async_read_until 读取的内容。所以我将实际字节数传递给回调。 不过,我正在考虑制作体长字段,大小为静态。谢谢。【参考方案2】:

async_read_until可以读取字节past the delimiter

备注

async_read_until 成功后 操作,streambuf 可能包含 超出分隔符的附加数据。 应用程序通常会离开 streambuf 中的数据 随后的 async_read_until 操作 检查。

【讨论】:

非常感谢。 async_read_until() 太明显了,所以我没有阅读它的整个文档。 不仅仅是“可以”,它很可能会。它尝试读取 streambuf 当前的空闲容量,但最多 65536。read_until(),read_size_helper()。

以上是关于当 streambuf 由先前的 async_read 填充时, boost::asio::async_read 进入 boost::asio::streambuf 块的主要内容,如果未能解决你的问题,请参考以下文章

❥关于C++之streambuf

❥关于C++之streambuf

将 streambuf 的内容复制到字符串

实现 std::basic_streambuf 子类来操作输入

提升 streambuf 并编写奇怪的行为

boost::asio::streambuf 通过 https 检索 xml 数据