我需要一个从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));
}
}
}