如何访问boost :: asio :: streamBuf输入作为字符串
How to access boost::asio::streambuf input as a string?
我正在编写一个非常简单的http服务器,基于:http://www.boost.org/doc/doc/libs/1_62_0/doc/doc/doc/html/boost_asio/exame/example/cpp11/echo/async_tcp_echo_server.cpp
我尝试了许多技术来从Boost :: Asio :: StreamBuf中提取数据,以解析HTTP标头。StreamBuf对象似乎无法正确管理其内存(或更有可能使用它)&我最终遇到了SEG错误。
您可以从代码中看到,此处或此处都没有建议的技术。我怀疑这是因为我正在使用boost::asio::async_read_until()
读取所有标题,而不仅仅是一个标题,因为大多数其他编码器似乎都在做。
任何建议/指示都将不胜感激。
/*
g++ -D TRY1 -ggdb3 -I $e/boost-1.62/include /tmp/streambuf.bug.cc $e/boost-1.62/lib/libboost_system.a -D TRY1
or
g++ -D TRY2 -ggdb3 -I $e/boost-1.62/include /tmp/streambuf.bug.cc $e/boost-1.62/lib/libboost_system.a -D TRY2
or
g++ -D TRY3 -ggdb3 -I $e/boost-1.62/include /tmp/streambuf.bug.cc $e/boost-1.62/lib/libboost_system.a -D TRY3
*/
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket) : socket_(std::move(socket)), dbg(true) {
assert(headers.empty());
memset(padding, 0, 10000);
std::cout << "buffer size: " << buffer.max_size() << 'n';
}
void start() { readHeaders(); }
private:
void readHeaders() {
if (dbg)
std::cout << "readHeaders() startn";
//auto self(shared_from_this());
boost::asio::async_read_until(socket_, buffer, "rnrn", [this](const boost::system::error_code &ec, std::size_t length) {
if (dbg)
std::cout << "Read " << length << " bytes of headers in async_read_until()n";
if (ec)
throw std::runtime_error("Error code in readHeaders()");
#ifdef TRY1
std::istream stream(&buffer);
std::string str;
assert(!corrupted("A1"));
while (std::getline(stream, str)) { // seg fault! (on 3rd invocation)
assert(!corrupted("A2"));
std::cout << "str=" << str << 'n';
assert(!corrupted("A3"));
}
#endif
#if 0
std::string str;
boost::asio::buffer_copy(boost::asio::buffer(str), buffer); // ugh, won't compile
#endif
#if 0
std::vector<unsigned char> v(buffer.size());
boost::asio::buffer_copy(boost::asio::buffer(v), buffer); // ugh, won't compile
const std::string str(v.begin(), v.end());
#endif
#ifdef TRY2
std::string str;
auto data = buffer.data();
assert(!corrupted("B1"));
for (auto it = data.begin(); it != data.end(); ++it) {
const auto buf = *it;
std::cout << "buf_size=" << boost::asio::buffer_size(buf) << 'n';
assert(!corrupted("B2"));
const char *tmp = boost::asio::buffer_cast<const char *>(buf);
assert(!corrupted("B3"));
str.append(tmp); // BUG!!
assert(!corrupted("B4")); // fails
}
#endif
#ifdef TRY3
auto data = buffer.data();
auto end = data.end();
std::string str;
assert(!corrupted("C1"));
for (auto it = data.begin(); it != end; ++it) {
assert(!corrupted("C2"));
std::vector<unsigned char> v(boost::asio::buffer_size(*it));
assert(!corrupted("C3"));
boost::asio::buffer_copy(boost::asio::buffer(v), *it); // BUG!!
assert(!corrupted("C4")); // fails
str.append(v.begin(), v.end());
assert(!corrupted("C5"));
}
#endif
#ifdef TRY4
assert(!corrupted("D1"));
const std::string str(boost::asio::buffers_begin(buffer.data()), boost::asio::buffers_end(buffer.data())); // BUG!!
assert(!corrupted("D2")); // fails
#endif
#ifdef TRY5
assert(!corrupted("E1"));
const std::string str((std::istreambuf_iterator<char>(&buffer)), std::istreambuf_iterator<char>()); // seg faults!
assert(!corrupted("E2"));
#endif
#ifdef TRY6
boost::asio::streambuf::const_buffers_type bufs = buffer.data();
assert(!corrupted("F1"));
std::string str(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs) + buffer.size()); // BUG!!
assert(!corrupted("F2")); // fails
#endif
assert(!corrupted("Z1"));
std::cout << "str=" << str << "end of datan";
std::istringstream input(str);
std::string line;
while (std::getline(input, line)) {
assert(!corrupted("Z2"));
if (line.size() == 1)
continue; // blank line
if (line.substr(0, 3) == "GET")
continue; // TODO: handle properly
const auto idx = line.find(':');
assert(idx != std::string::npos);
const std::string key(line.begin(), line.begin() + idx);
const std::string val(line.begin() + idx + 2, line.end());
// std::cout << "key=" << key << " val=" << val << 'n';
assert(!corrupted("Z3"));
headers[key] = val;
assert(!corrupted("Z4"));
}
assert(!corrupted("Z5"));
for (auto it3 : headers) {
std::cout << it3.first << '=' << it3.second << 'n';
}
const auto it2 = headers.find("Content Length");
contentLength = (it2 == headers.end() ? 0 : atoi(it2->second.c_str()));
if (contentLength > 0) {
const boost::system::error_code ec; // (boost::system::errc::success);
readBody(ec);
}
});
if (dbg)
std::cout << "readHeaders() endn";
}
void readBody (const boost::system::error_code &ec) {
if (dbg)
std::cout << "readBody()n";
if (ec)
throw std::runtime_error("Error code in readBody()");
boost::asio::streambuf::const_buffers_type bufs = buffer.data();
body.append(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs) + buffer.size());
if (dbg)
std::cout << "body.size=" << body.size() << " content length=" << contentLength << 'n';
boost::asio::async_read(socket_,
buffer,
boost::asio::transfer_at_least(1),
boost::bind(&session::readBody, this, boost::asio::placeholders::error));
}
bool corrupted (const std::string s) const {
bool b = false;
if (strlen(padding) > 0) {
std::cout << "buffer overflow detected @ " << s << "! padding is: " << padding << 'n';
std::cout.flush();
b = true;
}
if (headers.size() > 1000) {
std::cout << headers.size() << " headers!!n";
b = true;
}
return b;
}
tcp::socket socket_;
boost::asio::streambuf buffer;
char padding[10000]; // $buffer appears not to manage it's memory properly. Add some padding to detect overflows.
std::map<std::string, std::string> headers;
uint contentLength;
std::string body;
const bool dbg;
};
class server
{
public:
server(boost::asio::io_service& io_service, short port) : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
if (!ec) {
std::cout << "Connection acceptedn";
std::make_shared<session>(std::move(socket_))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: async_tcp_echo_server <port>n";
return 1;
}
boost::asio::io_service io_service;
server s(io_service, std::atoi(argv[1]));
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "n";
}
return 0;
}
我在Linux上使用Boost V1.62,GCC V6.1(Ubuntu 12.04)。
-
您可以从StreamBuf阅读
- 手动(请参阅文档)
-
使用
std::istream
:boost::asio::streambuf buf; std::istream is(&buf); // usual extraction: int i; if (is >> i) { // use `i` } // or usual line-wise extraction: std::string line; while (std::getline(is, line)) { // do something with `line` }
-
替代用法
boost::asio::buffer_*
函数(buffer_begin()
,buffer_end()
和buffer_copy
) - 如何复制或重复使用boost :: Asio :: astreambuf?
复制boost::asio::streambuf
的内容为字符串,在副本A StreamBuf的副本中都回答了字符串,以及如何复制或重复使用Boost :: Asio :: Asio :: streamBuf问题:
boost::asio::streambuf source; ... std::string target{buffers_begin(source.data()), buffers_end(source.data())};
观察到的问题是不确定行为的结果。该程序无法满足async_read_until()
的b
参数的生命周期要求,因为在调用完成处理程序之前,streambuf
被销毁:
[...]溪流的所有权由呼叫者保留,必须保证其在称为处理程序之前保持有效。
在这种情况下,streambuf
是session
的数据成员,session
对象由共享指针管理。唯一在以下表达式中创建和破坏了管理session
的唯一共享指针:
std::make_shared<session>(std::move(socket_))->start();
在start()
中,启动了async_read_until()
操作。但是,在返回表格start()
后,session
的buffer
在调用async_read_until()
的完成处理程序之前被销毁,违反了终身要求。
官方ASIO示例使用的惯用解决方案是捕获shared_from_this()
在完成处理程序的Lambda捕获中的结果。这可以确保session
的寿命至少与完成处理程序一样长。
auto self(shared_from_this());
async_read_until(socket_, buffer_, ...,
[this, self](boost::system::error_code& ec, std::size_t length)
{
// `self` keeps the `session` alive for the lifetime of the
// handler. If more async operations are initiated from within
// this handler, then the completion handlers should capture
// `self` as well.
...
});
您问题的确切答案给出了Sehe。以下是我目前用于解析标头的一些伪代码。
// This is where to store headers.
_STL::map<_STL::string, _STL::string> m_headers;
// buffer is of type asio::streambuf and contains response from asio::async_read_until
_STL::istream response_stream_headers(&buffer);
// Some helper variables.
_STL::string header, header_name, header_value;
while (true) {
_STL::getline(response_stream_headers, header, 'r');
// Remove n symbol from the stream.
response_stream_headers.get();
if (header == "") {
// We reached end of headers, there might be still some more data!!
break;
}
// Parse header to key->value
size_t separator_pos = header.find(':');
if (separator_pos != _STL::string::npos) {
header_name = header.substr(0, separator_pos);
if (separator_pos < header.length() - 1) {
header_value = header.substr(separator_pos + 1);
}
else {
header_value = "";
}
boost::trim_left(header_value);
m_headers[name] = value;
}
}
// Parsing is done, but some of the request response could have been reed by
// asio::async_read_until, so whe read response_stream_headers untill end.
// You should use body_response_start as the begining of your response.
std::string body_response_start(std::istreambuf_iterator<char>(response_stream_headers), {});
- 将 boost 序列化对象的 asio::streambuf 表示转换为 Beast 的 DynamicBody req.body()
- Writing uin32_t to asio::streambuf
- Boost::asio::streambuf consumption() 不清空缓冲区
- 将 std::string 转换为 boost::asio::streambuf
- Boost ASIO ForwardIterator for streambuf
- boost::asio::streambuf::consume-注入垃圾字符
- 从boost asio streambuf读取会将字节保持在缓冲区中
- 如何访问boost :: asio :: streamBuf输入作为字符串
- 如何复制或重复使用boost :: asio :: streambuf
- boost::asio::streambuf-如何重用缓冲区
- BOOST :: ASIO :: StreamBuf收缩到合适
- boost::asio 从套接字读取 n 个字节到 Streambuf
- streambuf with boost::asio::async_write
- Working with boost::asio::streambuf
- 使用带有boost::asio异步操作的自定义streambuf
- 从 streambuf 使用 boost::asio::ip::tcp 将数据部分写入 TCP 套接字
- 使用 streambuf 作为提升 asio 读写的缓冲区
- 如何将 boost::asio::streambuf 转换为浮点数
- 将数据从 boost::asio::streambuf 复制到 std::string
- 如何"prepare "字符数组来提升::asio::streambuf::mutable_buffers_type缓冲区?