寻找一个boost :: asio(以及自己的提升)决定编写异步服务器。要存储传入的数据,我使用boost :: asio :: streambuf。 我有一个问题。当我从客户端收到第二条消息后,我发现缓冲区中包含来自先前消息的数据。 虽然我在输入缓冲区调用Consume方法。怎么了?
class tcp_connection
// Using shared_ptr and enable_shared_from_this
// because we want to keep the tcp_connection object alive
// as long as there is an operation that refers to it.
: public boost::enable_shared_from_this<tcp_connection>
{
...
boost::asio::streambuf receive_buffer;
boost::asio::io_service::strand strand;
}
...
void tcp_connection::receive()
{
// Read the response status line. The response_ streambuf will
// automatically grow to accommodate the entire line. The growth may be
// limited by passing a maximum size to the streambuf constructor.
boost::asio::async_read_until(m_socket, receive_buffer, "\r\n",
strand.wrap(boost::bind(&tcp_connection::handle_receive, shared_from_this()/*this*/,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)));
}
void tcp_connection::handle_receive(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
if (!error)
{
// process the data
/* boost::asio::async_read_until remarks
After a successful async_read_until operation,
the streambuf may contain additional data beyond the delimiter.
An application will typically leave that data in the streambuf for a
subsequent async_read_until operation to examine.
*/
/* didn't work
std::istream is(&receive_buffer);
std::string line;
std::getline(is, line);
*/
// clean up incomming buffer but it didn't work
receive_buffer.consume(bytes_transferred);
receive();
}
else if (error != boost::asio::error::operation_aborted)
{
std::cout << "Client Disconnected\n";
m_connection_manager.remove(shared_from_this());
}
}
答案 0 :(得分:21)
使用std::istream
并从中读取,例如std::getline()
或明确调用boost::asio::streambuf::consume(n)
,将从输入序列中删除数据。
如果应用程序执行这些操作中的任何一个并且后续read_until()
操作导致receive_buffer
的输入序列中的重复数据,则复制的数据可能源自远程对等方。如果远程对等体正在写入套接字并直接使用streambuf的输入序列,则远程对等体需要在每次成功写入操作后显式调用consume()
。
如文档中所述,成功的read_until()
操作可能包含分隔符之外的其他数据,包括其他分隔符。例如,如果将"a@b@"
写入套接字,则使用read_until()
作为分隔符的'@'
操作可以读取并将"a@b@"
提交给streambuf的输入序列。但是,该操作将指示传输的字节数是直到并包括第一个分隔符。因此,bytes_transferred
为2
,streambuf.size()
为4
。消耗2
个字节后,streambuf的输入序列将包含"b@"
,随后对read_until()
的调用将立即返回,因为streambuf已经包含分隔符。 / p>
以下是用于读写的完整示例demonstrating streambuf
,以及输入序列的使用方式:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}
std::string make_string(boost::asio::streambuf& streambuf)
{
return {buffers_begin(streambuf.data()),
buffers_end(streambuf.data())};
}
int main()
{
using boost::asio::ip::tcp;
boost::asio::io_service io_service;
// Create all I/O objects.
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket server_socket(io_service);
tcp::socket client_socket(io_service);
// Connect client and server sockets.
acceptor.async_accept(server_socket, boost::bind(&noop));
client_socket.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
io_service.run();
// Write to server.
boost::asio::streambuf write_buffer;
std::ostream output(&write_buffer);
output << "a@"
"b@";
write(server_socket, write_buffer.data());
std::cout << "Wrote: " << make_string(write_buffer) << std::endl;
assert(write_buffer.size() == 4); // Data not consumed.
// Read from the client.
boost::asio::streambuf read_buffer;
// Demonstrate consuming via istream.
{
std::cout << "Read" << std::endl;
auto bytes_transferred = read_until(client_socket, read_buffer, '@');
// Verify that the entire write_buffer (data pass the first delimiter) was
// read into read_buffer.
auto initial_size = read_buffer.size();
assert(initial_size == write_buffer.size());
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::istream input(&read_buffer);
std::string line;
getline(input, line, '@'); // Consumes from the streambuf.
assert("a" == line); // Note getline discards delimiter.
std::cout << "Read consumed: " << line << "@" << std::endl;
assert(read_buffer.size() == initial_size - bytes_transferred);
}
// Write an additional message to the server, but only consume 'a@'
// from write buffer. The buffer will contain 'b@c@'.
write_buffer.consume(2);
std::cout << "Consumed write buffer, it now contains: " <<
make_string(write_buffer) << std::endl;
assert(write_buffer.size() == 2);
output << "c@";
assert(write_buffer.size() == 4);
write(server_socket, write_buffer.data());
std::cout << "Wrote: " << make_string(write_buffer) << std::endl;
// Demonstrate explicitly consuming via the streambuf.
{
std::cout << "Read" << std::endl;
auto initial_size = read_buffer.size();
auto bytes_transferred = read_until(client_socket, read_buffer, '@');
// Verify that the read operation did not attempt to read data from
// the socket, as the streambuf already contained the delimiter.
assert(initial_size == read_buffer.size());
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::string line(
boost::asio::buffers_begin(read_buffer.data()),
boost::asio::buffers_begin(read_buffer.data()) + bytes_transferred);
assert("b@" == line);
assert(read_buffer.size() == initial_size); // Nothing consumed.
read_buffer.consume(bytes_transferred); // Explicitly consume.
std::cout << "Read consumed: " << line << std::endl;
assert(read_buffer.size() == 0);
}
// Read again.
{
std::cout << "Read" << std::endl;
read_until(client_socket, read_buffer, '@');
// Read from the streambuf.
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
std::istream input(&read_buffer);
std::string line;
getline(input, line, '@'); // Consumes from the streambuf.
assert("b" == line); // Note "b" is expected and not "c".
std::cout << "Read consumed: " << line << "@" << std::endl;
std::cout << "Read buffer contains: " << make_string(read_buffer)
<< std::endl;
}
}
输出:
Wrote: a@b@
Read
Read buffer contains: a@b@
Read consumed: a@
Consumed write buffer, it now contains: b@
Wrote: b@c@
Read
Read buffer contains: b@
Read consumed: b@
Read
Read buffer contains: b@c@
Read consumed: b@
Read buffer contains: c@