使用streambuf

时间:2017-01-09 01:31:30

标签: c++11 boost-asio iostream

我需要一个从tcp源读取XML行分隔数据的循环的帮助,我在部分行中遇到std :: getline()读取问题。在循环中,我创建了一个boost::asio::streambuf,我通过boost::asio::read_until("\r\n")阅读。然后,一旦完成,我从相关的streambuf读取行。

我的循环的基本流程如下所示。

基本思想是来自远程源的tcp数据是面向行的。开始和结束<screen>...</screen>的行是XML格式的(来自远程TCP服务器的所有内容总是在单独的'\ r \ n'分隔行上读入)。一旦我遇到这一行,我设置一个标志来捕获截止行,然后我用XML标题和&amp ;;解析内容。我还读了一些其他非XML行,如else块所示。

这通常可以正常工作,但是在循环中我会遇到部分行读取,我不知道为什么。我认为boost::asio::read_until会将一行数据读入boost::asio::streambuf响应对象,我可以从中可靠地调用std::getline()。我注意到在调试期间,getline经常无法读取整行,我不确定为什么会发生这种情况。

此外,我在streambuf中测试eof()的行有时也是正确的,在这种情况下我循环继续,我将剩余的部分行读入一个新的堆栈分配的streambuf(但我不确定我是不是丢弃先前streambuf的东西)。有没有一些首选的方法来做到这一点,我在boost文档中搜索了一些类似的例子,但我没有找到类似的东西。我也不确定套接字是否阻塞对此有什么影响,但在这种特殊情况下我无法阻止调用。

auto bCapturingXML = false;
std::vector<std::string> screenLines;
std::string partialLine, line;
while (!mShutdownFlag) {
    if (!socket) {
        socket = std::make_unique<tcp::socket>(ios);
        // Put the socket into non-blocking mode.
        socket->connect(endpoint, ec);
        tcp::socket::non_blocking_io non_blocking_io(true);
        socket->io_control(non_blocking_io);
        if (ec == error::connection_refused) {
            socket.reset();
            std::this_thread::sleep_for(milliseconds(500));
        }
    } else {
        // read_until we have a match to the following
        boost::asio::streambuf response;
        boost::asio::read_until(*socket, response, "\r\n", ec);
        if (!ec) {
            std::istream prosimXMLStream(&response);
            while (std::getline(prosimXMLStream, partialLine)) {
                // handle the empty line case
                line += partialLine;
                if (line.find_last_of('\r') != std::string::npos) {
                    if (line.length() > 1) {
                        if (!bCapturingXML && (line.find(
                                    "<screen>") != std::string::npos)) {
                            bCapturingXML = true;
                            screenLines.clear();
                            static const std::string gXMLDeclaration =
                                "<?xml version=\"1.0\" standalone='yes' >\n";
                            screenLines.push_back(gXMLDeclaration);
                            screenLines.emplace_back(std::move(line));
                        } else if (bCapturingXML && (line.find(
                                    "</screen>") != std::string::npos)) {
                            bCapturingXML = false;
                            screenLines.emplace_back(std::move(line));
                            // convert the screenLines to a big XML string
                            // suitable for parsing via TinyXML
                            std::stringstream ss;
                            std::move(screenLines.cbegin(), screenLines.cend(),
                                      std::ostream_iterator<std::string>(ss));
                            // reset the screenLines
                            screenLines.clear();
                            // parse the XML screen representation
                            TiXmlDocument screenDoc("screen.xml");
                            auto fileContent = std::move(ss.str());
                            screenDoc.Parse(fileContent.c_str());
                            if (!screenDoc.Error()) {
                                // get the updated page, passing in the previous one
                                handleScreenUpdate(screenDoc, vcduPage);
                            }
                        } else if (bCapturingXML) {
                            screenLines.emplace_back(std::move(line));
                        } else {
                            // capture other lines outside the
                            ...
                            line.clear();
                        }
                    } else {
                        // empty '\r\n' only line
                        LOG_INFO(gLogger, gChannel) << boost::format(
                            "%1%: empty line")
                            % __FUNCTION__;
                    }
                } else {
                    // error: partial line
                    LOG_INFO(gLogger, gChannel) << boost::format(
                        "%1%: partial line read: %2%")
                        % __FUNCTION__
                        % partialLine;
                    partialLine.clear();
                }
            }
            if (prosimXMLStream.eof()) {
                // need to re enter the loop and re-read into a
                // new prosimXMLStream
            }
        } else if (ec == error::would_block) {
            // non blocking socket mode - sleep
            std::this_thread::sleep_for(milliseconds(100));
        }
    }
}

0 个答案:

没有答案