关于在 async_read_until 之后立即使用 async_write 的问题
issues about using async_write right after async_read_until
我的代码如下:
boost::asio::streambuf b1;
boost::asio::async_read_until(upstream_socket_, b1, '@',
boost::bind(&bridge::handle_upstream_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
void handle_upstream1_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(downstream_socket_,
b2,
boost::bind(&bridge::handle_downstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
根据 async_read_until、http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/async_read_until/overload1.html 的文档,
async_read_until 操作成功后,streambuf 可能包含分隔符之外的其他数据。应用程序通常会将该数据留在 streambuf 中,以供后续 async_read_until 操作检查。
我知道 streambuf 可能包含超出定界符的额外数据,但是,在我的例子中,它会将这些额外数据(超出 char'@' 的数据)写入 downstream_socket_ 中的 async_write操作?或者 async_write 函数是否足够聪明,不会在下次调用 handle_upstream1_read 函数之前不写入这些额外数据?
根据文档中的方法,streambuf中的数据首先存储在istream中( std::istream response_stream(&streambuf); )
然后使用 std::getline() 函数将其放入字符串中。
我真的需要先将 streambuf 存储在 istream 中,然后将其转换为字符串,然后再将其转换回 char 数组(以便我可以将 char 数组发送到 downstream_socket_ )而不是仅使用 async_write 将数据(直到但不包括定界符 '@' )写入 downstream_socket_ ?
我更喜欢第二种方法,这样我就不需要对数据进行多次转换。但是,当我尝试第二种方法时,似乎出了点问题。
我的理想情况是:
- upstream_socket_ 使用 async_read_until
收到 xxxx@yyyy
- xxxx@ 写入 downstream_socket_
- upstream_socket_ 使用 async_read_until
收到 zzzz@kkkk
- yyyyzzzz@ 写入 downstream_socket_
似乎async_write操作仍然将超出分隔符的数据写入downstream_socket_。 (但我对此不是 100% 确定)
如果有人能提供一点帮助,我将不胜感激!
async_write()
overload being used is considered complete when all of the streambuf
的数据,它的输入序列,已经被写入WriteStream(套接字)。相当于调用:
boost::asio::async_write(stream, streambuf,
boost::asio::transfer_all(), handler);
可以通过调用此 async_write()
overload with the boost::asio::transfer_exactly
完成条件来限制从 streambuf 对象写入和消耗的字节数:
boost::asio::async_write(stream, streambuf,
boost::asio::transfer_exactly(n), handler);
或者,可以直接从 streambuf 的输入序列写入。但是,需要明确地从 streambuf 中消费。
boost::asio::async_write(stream,
boost::asio::buffer(streambuf.data(), n), handler);
// Within the completion handler...
streambuf.consume(n);
请注意,当 async_read_until()
操作完成时,完成处理程序的 bytes_transferred
参数包含 streambuf 的输入序列中的字节数,直至并包括分隔符,或者 0
如果发生错误。
这是一个使用这两种方法的完整示例 demonstrating。该示例使用同步操作编写,试图简化流程:
#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() {}
/// @brief Helper function that extracts a string from a streambuf.
std::string make_string(
boost::asio::streambuf& streambuf,
std::size_t n)
{
return std::string(
boost::asio::buffers_begin(streambuf.data()),
boost::asio::buffers_begin(streambuf.data()) + n);
}
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();
// Mockup write_buffer as if it read "xxxx@yyyy" with read_until()
// using '@' as a delimiter.
boost::asio::streambuf write_buffer;
std::ostream output(&write_buffer);
output << "xxxx@yyyy";
assert(write_buffer.size() == 9);
auto bytes_transferred = 5;
// Write to server.
boost::asio::write(server_socket, write_buffer,
boost::asio::transfer_exactly(bytes_transferred));
// Verify write operation consumed part of the input sequence.
assert(write_buffer.size() == 4);
// Read from client.
boost::asio::streambuf read_buffer;
bytes_transferred = boost::asio::read(
client_socket, read_buffer.prepare(bytes_transferred));
read_buffer.commit(bytes_transferred);
// Copy from the read buffers input sequence.
std::cout << "Read: " <<
make_string(read_buffer, bytes_transferred) << std::endl;
read_buffer.consume(bytes_transferred);
// Mockup write_buffer as if it read "zzzz@kkkk" with read_until()
// using '@' as a delimiter.
output << "zzzz@kkkk";
assert(write_buffer.size() == 13);
bytes_transferred = 9; // yyyyzzzz@
// Write to server.
boost::asio::write(server_socket, buffer(write_buffer.data(),
bytes_transferred));
// Verify write operation did not consume the input sequence.
assert(write_buffer.size() == 13);
write_buffer.consume(bytes_transferred);
// Read from client.
bytes_transferred = boost::asio::read(
client_socket, read_buffer.prepare(bytes_transferred));
read_buffer.commit(bytes_transferred);
// Copy from the read buffers input sequence.
std::cout << "Read: " <<
make_string(read_buffer, bytes_transferred) << std::endl;
read_buffer.consume(bytes_transferred);
}
输出:
Read: xxxx@
Read: yyyyzzzz@
其他一些注意事项:
streambuf
拥有内存,std::istream
和std::ostream
使用内存。当需要提取格式化输入或插入格式化输出时,使用流可能是一个好主意。例如,当希望将字符串 "123"
读取为整数 123
.
可以直接访问 streambuf 的输入序列并对其进行迭代。在上面的示例中,我使用 boost::asio::buffers_begin()
通过迭代 streambuf 的输入序列来帮助构建 std::string
。
std::string(
boost::asio::buffers_begin(streambuf.data()),
boost::asio::buffers_begin(streambuf.data()) + n);
正在使用基于流的传输协议,因此将传入数据作为流处理。请注意,即使中间服务器重构消息并在一次写入操作中发送 "xxxx@"
并在后续写入操作中发送 "yyyyzzzz@"
,下游也可能在单个读取操作中读取 "xxxx@yyyy"
。
我的代码如下:
boost::asio::streambuf b1;
boost::asio::async_read_until(upstream_socket_, b1, '@',
boost::bind(&bridge::handle_upstream_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
void handle_upstream1_read(const boost::system::error_code& error,
const size_t& bytes_transferred)
{
if (!error)
{
async_write(downstream_socket_,
b2,
boost::bind(&bridge::handle_downstream_write,
shared_from_this(),
boost::asio::placeholders::error));
}
else
close();
}
根据 async_read_until、http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/async_read_until/overload1.html 的文档, async_read_until 操作成功后,streambuf 可能包含分隔符之外的其他数据。应用程序通常会将该数据留在 streambuf 中,以供后续 async_read_until 操作检查。
我知道 streambuf 可能包含超出定界符的额外数据,但是,在我的例子中,它会将这些额外数据(超出 char'@' 的数据)写入 downstream_socket_ 中的 async_write操作?或者 async_write 函数是否足够聪明,不会在下次调用 handle_upstream1_read 函数之前不写入这些额外数据?
根据文档中的方法,streambuf中的数据首先存储在istream中( std::istream response_stream(&streambuf); ) 然后使用 std::getline() 函数将其放入字符串中。
我真的需要先将 streambuf 存储在 istream 中,然后将其转换为字符串,然后再将其转换回 char 数组(以便我可以将 char 数组发送到 downstream_socket_ )而不是仅使用 async_write 将数据(直到但不包括定界符 '@' )写入 downstream_socket_ ?
我更喜欢第二种方法,这样我就不需要对数据进行多次转换。但是,当我尝试第二种方法时,似乎出了点问题。
我的理想情况是:
- upstream_socket_ 使用 async_read_until 收到 xxxx@yyyy
- xxxx@ 写入 downstream_socket_
- upstream_socket_ 使用 async_read_until 收到 zzzz@kkkk
- yyyyzzzz@ 写入 downstream_socket_
似乎async_write操作仍然将超出分隔符的数据写入downstream_socket_。 (但我对此不是 100% 确定)
如果有人能提供一点帮助,我将不胜感激!
async_write()
overload being used is considered complete when all of the streambuf
的数据,它的输入序列,已经被写入WriteStream(套接字)。相当于调用:
boost::asio::async_write(stream, streambuf,
boost::asio::transfer_all(), handler);
可以通过调用此 async_write()
overload with the boost::asio::transfer_exactly
完成条件来限制从 streambuf 对象写入和消耗的字节数:
boost::asio::async_write(stream, streambuf,
boost::asio::transfer_exactly(n), handler);
或者,可以直接从 streambuf 的输入序列写入。但是,需要明确地从 streambuf 中消费。
boost::asio::async_write(stream,
boost::asio::buffer(streambuf.data(), n), handler);
// Within the completion handler...
streambuf.consume(n);
请注意,当 async_read_until()
操作完成时,完成处理程序的 bytes_transferred
参数包含 streambuf 的输入序列中的字节数,直至并包括分隔符,或者 0
如果发生错误。
这是一个使用这两种方法的完整示例 demonstrating。该示例使用同步操作编写,试图简化流程:
#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() {}
/// @brief Helper function that extracts a string from a streambuf.
std::string make_string(
boost::asio::streambuf& streambuf,
std::size_t n)
{
return std::string(
boost::asio::buffers_begin(streambuf.data()),
boost::asio::buffers_begin(streambuf.data()) + n);
}
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();
// Mockup write_buffer as if it read "xxxx@yyyy" with read_until()
// using '@' as a delimiter.
boost::asio::streambuf write_buffer;
std::ostream output(&write_buffer);
output << "xxxx@yyyy";
assert(write_buffer.size() == 9);
auto bytes_transferred = 5;
// Write to server.
boost::asio::write(server_socket, write_buffer,
boost::asio::transfer_exactly(bytes_transferred));
// Verify write operation consumed part of the input sequence.
assert(write_buffer.size() == 4);
// Read from client.
boost::asio::streambuf read_buffer;
bytes_transferred = boost::asio::read(
client_socket, read_buffer.prepare(bytes_transferred));
read_buffer.commit(bytes_transferred);
// Copy from the read buffers input sequence.
std::cout << "Read: " <<
make_string(read_buffer, bytes_transferred) << std::endl;
read_buffer.consume(bytes_transferred);
// Mockup write_buffer as if it read "zzzz@kkkk" with read_until()
// using '@' as a delimiter.
output << "zzzz@kkkk";
assert(write_buffer.size() == 13);
bytes_transferred = 9; // yyyyzzzz@
// Write to server.
boost::asio::write(server_socket, buffer(write_buffer.data(),
bytes_transferred));
// Verify write operation did not consume the input sequence.
assert(write_buffer.size() == 13);
write_buffer.consume(bytes_transferred);
// Read from client.
bytes_transferred = boost::asio::read(
client_socket, read_buffer.prepare(bytes_transferred));
read_buffer.commit(bytes_transferred);
// Copy from the read buffers input sequence.
std::cout << "Read: " <<
make_string(read_buffer, bytes_transferred) << std::endl;
read_buffer.consume(bytes_transferred);
}
输出:
Read: xxxx@
Read: yyyyzzzz@
其他一些注意事项:
streambuf
拥有内存,std::istream
和std::ostream
使用内存。当需要提取格式化输入或插入格式化输出时,使用流可能是一个好主意。例如,当希望将字符串"123"
读取为整数123
.可以直接访问 streambuf 的输入序列并对其进行迭代。在上面的示例中,我使用
boost::asio::buffers_begin()
通过迭代 streambuf 的输入序列来帮助构建std::string
。std::string( boost::asio::buffers_begin(streambuf.data()), boost::asio::buffers_begin(streambuf.data()) + n);
正在使用基于流的传输协议,因此将传入数据作为流处理。请注意,即使中间服务器重构消息并在一次写入操作中发送
"xxxx@"
并在后续写入操作中发送"yyyyzzzz@"
,下游也可能在单个读取操作中读取"xxxx@yyyy"
。