2014-07-11 42 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

相關問題