我目前正在使用 Boost Asio 实现网络协议(protocol).域类已经存在,我能够
- 将数据包写入
std::istream
- 并从
std::ostream
中读取数据包。
一个网络包包含一个网络包头。 header 以 Packet Length 字段开始,其大小为两个字节 (std::uint16_t
)。
我使用 TCP/IPv4 作为传输层,因此我尝试实现以下内容:
- 读取数据包的长度以了解其总长度。这意味着从套接字中读取两个字节。
- 阅读数据包的其余部分。这意味着从套接字中准确读取
kActualPacketLength - sizeof(PacketLengthFieldType)
个字节。 - Concat 读取二进制数据。
因此我至少需要调用两次 boost::asio::read
(我是同步开始的!)。
如果我对预期长度进行硬编码,我可以通过调用 boost::asio::read
来读取数据包:
Packet const ReadPacketFromSocket() {
boost::asio::streambuf stream_buffer;
boost::asio::streambuf::mutable_buffers_type buffer{
stream_buffer.prepare(Packet::KRecommendedMaximumSize)};
std::size_t const kBytesTransferred{boost::asio::read(
this->socket_,
buffer,
// TODO: Remove hard-coded value.
boost::asio::transfer_exactly(21))};
stream_buffer.commit(kBytesTransferred);
std::istream input_stream(&stream_buffer);
PacketReader const kPacketReader{MessageReader::GetInstance()};
return kPacketReader.Read(input_stream);
}
这会立即读取完整的数据包数据并返回一个 Packet
实例。这行得通,所以这个概念行得通。
到目前为止一切顺利。现在我的问题:
如果我使用相同的 boost::asio::streambuf
连续两次调用 boost::asio::read
,我将无法正常工作。
代码如下:
Packet const ReadPacketFromSocket() {
std::uint16_t constexpr kPacketLengthFieldSize{2};
boost::asio::streambuf stream_buffer;
boost::asio::streambuf::mutable_buffers_type buffer{
stream_buffer.prepare(Packet::KRecommendedMaximumSize)};
std::size_t const kBytesTransferred{boost::asio::read(
// The stream from which the data is to be read.
this->socket_,
// One or more buffers into which the data will be read.
buffer,
// The function object to be called to determine whether the read
// operation is complete.
boost::asio::transfer_exactly(kPacketLengthFieldSize))};
// The received data is "committed" (moved) from the output sequence to the
// input sequence.
stream_buffer.commit(kBytesTransferred);
BOOST_LOG_TRIVIAL(debug) << "bytes transferred: " << kBytesTransferred;
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
std::uint16_t packet_size;
// This does seem to modify the streambuf!
std::istream istream(&stream_buffer);
istream.read(reinterpret_cast<char *>(&packet_size), sizeof(packet_size));
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
BOOST_LOG_TRIVIAL(debug) << "data of stream_buffer: " << std::to_string(packet_size);
std::size_t const kBytesTransferred2{
boost::asio::read(
this->socket_,
buffer,
boost::asio::transfer_exactly(packet_size - kPacketLengthFieldSize))};
stream_buffer.commit(kBytesTransferred2);
BOOST_LOG_TRIVIAL(debug) << "bytes transferred: " << kBytesTransferred2;
BOOST_LOG_TRIVIAL(debug) << "size of stream_buffer: " << stream_buffer.size();
// Create an input stream with the data from the stream buffer.
std::istream input_stream(&stream_buffer);
PacketReader const kPacketReader{MessageReader::GetInstance()};
return kPacketReader.Read(input_stream);
}
我有以下问题:
- 在第一次套接字读取后从
boost::asio::streambuf
读取数据包长度似乎从boost::asio::streambuf
中删除了数据。 - 如果我使用两个不同的
boost::asio::streambuf
实例,我不知道如何“连接”/“追加”它们。
归根结底,我需要一个包含从套接字获取的正确数据的 std::istream
。
有人可以指导我正确的方向吗?我已经尝试让这项工作几个小时了......
也许这种方法不是最好的,所以我愿意接受改进我的设计的建议。
谢谢!
最佳答案
我相信这种行为是设计使然的。
要连接缓冲区,您可以使用 BUfferSequences(使用
make_buffers
)并使用缓冲区迭代器,或者您可以将第二个流式传输到第一个:boost::asio::streambuf a, b; std::ostream as(&a); as << &b;
现在您可以丢弃
b
,因为它的未决数据已附加到a
关于c++ - 通过连续两次调用 boost::asio::read 检索正确的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24695395/