2014-07-11 40 views
1

我目前正在使用Boost Asio实现一个网络协议。域类已经存在,并且我能够连续两次调用boost :: asio :: read检索正确的数据

  • 写入数据包发送到std::istream
  • 和读取从std::ostream包。

网络分组包含网络分组报头。头部以数据包长度字段开头,其字段大小为两个字节(std::uint16_t)。

我使用TCP/IPv4的作为传输层,因此我尝试执行以下操作:

  1. 读取数据包的长度,以了解它的总长度。这意味着只需从套接字读取两个字节。
  2. 阅读数据包的其余部分。这意味着从套接字准确读取kActualPacketLength - sizeof(PacketLengthFieldType)个字节。
  3. 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::readboost::asio::streambuf我不能得到它的工作。

下面是代码:

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); 
} 

我有以下问题:

  1. 读取来自boost::asio::streambuf数据包长度的第一插槽读取似乎从boost::asio::streambuf删除数据之后。
  2. 如果我使用两个截然不同的boost::asio::streambuf实例,我不知道如何“连接”/“追加”它们。

在一天结束时,我需要一个std::istream与从套接字获得正确的数据。

有人可以引导我进入正确的方向吗?我试图让这项工作现在几个小时...

也许这种方法不是最好的,所以我愿意提出改进我的设计的建议。

谢谢!

回答

3
  1. 我相信这种行为是通过设计。

  2. 要连接的缓冲区,你可以使用BUfferSequences(使用make_buffers),并使用缓冲迭代器,或者你可以流第二次进入第一:

    boost::asio::streambuf a, b; 
    std::ostream as(&a); 
    
    as << &b; 
    

    现在,你可以扔掉b,因为它是待处理的数据已追加到a

看到它Live on Coliru

+0

你确定你的代码编译?我无法让它与GCC 4.7.1和Boost 1.55.0一起工作。我还喜欢使用一个'streambuf'作为我的例子。 –

+0

我打错了'ostream' :)当然你可以有一个r/w流(记得在适当的时候寻找原点) – sehe

+0

你能详细说明吗?我确实需要一个'std :: istream'对象将它传递给我的PacketReader。另一个问题是,如果我从中读取数据包的长度,第一个'streambuf'就会被修改。是不是可以使用** 1 **'streambuf'来读取两个套接字(并从中读取'streambuf'),以便通过第二次读取附加'streambuf'? –

0

在我忘记之前,我想总结一下我当前的解决方案,它不使用boost::asio::streambuf,因为如果不修改它就不可能读取它。相反,我使用std::vector<std::uint8_t>ByteVector)作为缓冲区的数据存储器。

下面的源代码包含了我目前的解决方案:从使用这种方法两个进程之间我的域模型的消息

Packet const ReadPacketFromSocket() { 
    ByteVector const kPacketLengthData{this->ReadPacketLengthFromSocket()}; 
    PacketHeader::PacketLengthType kPacketLength{ 
     static_cast<PacketHeader::PacketLengthType>(
      (kPacketLengthData[1] << 8) | kPacketLengthData[0])}; 

    ByteVector rest_packet_data(Packet::KRecommendedMaximumSize); 
    boost::asio::read(
     this->socket_, 
     boost::asio::buffer(rest_packet_data), 
     boost::asio::transfer_exactly(
      kPacketLength - sizeof(PacketHeader::PacketLengthType))); 

    ByteVector data{ 
     VectorUtils::GetInstance().Concatenate(
      kPacketLengthData, 
      rest_packet_data)}; 

    // Create an input stream from the vector. 
    std::stringstream input_stream; 
    input_stream.rdbuf()->pubsetbuf(
     reinterpret_cast<char *>(&data[0]), data.size()); 

    PacketReader const kPacketReader{MessageReader::GetInstance()}; 

    return kPacketReader.Read(input_stream); 
} 

ByteVector ReadPacketLengthFromSocket() { 
    ByteVector data_holder(sizeof(PacketHeader::PacketLengthType)); 

    boost::asio::read(
     this->socket_, 
     boost::asio::buffer(data_holder), 
     boost::asio::transfer_exactly(sizeof(PacketHeader::PacketLengthType))); 

    return data_holder; 
} 

这个工程就像一个魅力,我已经成功地交换数据包。

但是:该解决方案感觉,因为我必须做大量的转换。也许别人可以提供一个更清洁的方法?你对我的解决方案有什么看法?

+0

“a boost :: asio :: streambuf,因为如果不修改它就不可能读取它” - 为什么你能够在不修改的情况下从流中读取数据它?也许你的意思是“读入它”而不覆盖它? – sehe

相关问题