Google :: protobuf + boost :: asio失败

时间:2013-11-07 15:29:59

标签: c++ linux boost-asio protocol-buffers

我研究了现有的例子:

  1. Sending Protobuf Messages with boost::asio
  2. Reading Protobuf objects using boost::asio::read_async
  3. Google Protocol Buffers: parseDelimitedFrom and writeDelimitedTo for C++
  4. Are there C++ equivalents for the Protocol Buffers delimited I/O functions in Java?
  5. Sending Protobuf Messages with boost::asio
  6. 但我仍然无法弄清楚如何使用Boost :: asio API传递Google Protobuf消息。特别是我对以下问题没有清楚的认识:

    1. boost :: asio :: streambuf和google :: protobuf :: io对象之间的交互(以及应用最后一个对象的必要性)
    2. 正确实现消息流(由于缺少c ++ API中的writeDelimitedTo和parseDelimitedFrom方法)
    3. 这是基于来自examples的boost :: asio v.1.39 ssl_client的实现。

          class client
      {
      public:
        client(boost::asio::io_service& io_service, boost::asio::ssl::context& context,
            boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
          : socket_(io_service, context),
              request_stream(&b),
              raw_output(&request_stream),
              coded_output(&raw_output)
        {
          ... 
        }
      
        void handle_connect(const boost::system::error_code& error,
            boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
        {
          ...
        }
      
        //Debugging function
        void print_buffers_condition(const char *step)
        {
            std::cout << "\nBuffer conditions after " << step << std::endl;
            std::cout << "boost::asio::streambuf\t\tb: " << b.size() << std::endl;
            std::cout << "google::protobuf::io::OstreamOutputStream raw_output: " << raw_output.ByteCount() << std::endl;
            std::cout << "google::protobuf::io::CodedOutputStream coded_output: " << coded_output.ByteCount() << std::endl;
            std::cout << std::endl;
        }
      
        //Sending test message after SSL Handshake
        void handle_handshake(const boost::system::error_code& error)
        {
            std::cout << "-----------------------------SENDING-----------------------------" << std::endl;
          print_buffers_condition("handle handshake");
          if (!error)
          {
              SearchRequest msg;
              msg.set_query("qwerty");
              msg.set_code(12345);
      
              std::cout << "Debugged" << std::endl;
              msg.PrintDebugString();
      
      
              //Writing the length of the message before and serializing                 
                          print_buffers_condition("before serialising");
              coded_output.WriteVarint32(msg.ByteSize());
              if (!msg.SerializeToCodedStream(&coded_output))
              {
                  std::cout << "serailizing error" << std::endl;
              }
              else
              {
                  std::cout << "serializing success" << std::endl;
              }
      
              //Sending
              buffers_condition("before async write");
              boost::asio::async_write(socket_,
                                       b,
                                       boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
              buffers_condition("after async write");
          }
          else
          {
            std::cout << "Handshake failed: " << error << "\n";
          }
        }
      
        void handle_write(const boost::system::error_code& error,
            size_t bytes_transferred)
        {
          std::cout << " bytes_trransferred: " << bytes_transferred << std::endl;
          if (!error)
          {
              std::cout << "No error" << std::endl;
              ...
          }
          else
          {
            std::cout << "Write failed: " << error << "\n";
          }
        }
      
        void handle_read(const boost::system::error_code& error,
            size_t bytes_transferred)
        {
          ...
        }
      
      private:
        boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
        boost::asio::streambuf b;
        std::ostream request_stream;
        google::protobuf::io::OstreamOutputStream raw_output;
        google::protobuf::io::CodedOutputStream coded_output;
      };
      

      此代码可操作,因此在创建消息后,我们会进入void handle_write(const boost::system::error_code& error, size_t bytes_transferred)函数。打印bytes_transferred_值返回0:服务器(在examples的基础上实现)也没有收到任何内容。

      调试函数void print_buffers_condition(const char *step)的使用暗示在通过一堆不同的缓冲对象传输时丢失消息:

          $ ./client 127.0.0.1 5000
      -----------------------------SENDING-----------------------------
      
      Buffer conditions after handle handshake
      boost::asio::streambuf      b: 0
      google::protobuf::io::OstreamOutputStream raw_output: 8192
      google::protobuf::io::CodedOutputStream coded_output: 0
      
      Debugged: 
      query: "qwerty"
      code: 12345
      
      Buffer conditions after before serialization
      boost::asio::streambuf      b: 0
      google::protobuf::io::OstreamOutputStream raw_output: 8192
      google::protobuf::io::CodedOutputStream coded_output: 0
      
      serializing success
      
      Buffer conditions after before async write
      boost::asio::streambuf      b: 0
      google::protobuf::io::OstreamOutputStream raw_output: 8192
      google::protobuf::io::CodedOutputStream coded_output: 13
      
      
      Buffer conditions after after async write
      boost::asio::streambuf      b: 0
      google::protobuf::io::OstreamOutputStream raw_output: 8192
      google::protobuf::io::CodedOutputStream coded_output: 13
      
       bytes_trransferred: 0
      

      我不知道如何以适当的方式做到这一点。 操作系统是RHEL 6.4。 谢谢。

1 个答案:

答案 0 :(得分:3)

我不熟悉asio,但在我看来,问题在于你没有冲洗缓冲区。数据停留在CodedOutputStream并且永远不会进入asio。

应该在堆栈上分配

CodedOutputStream,这样一旦你写完消息就会销毁它。析构函数将刷新缓冲区。请注意CodedOutputStream分配起来很便宜,因此将它放在堆栈上没有性能问题(事实上,它可能更好)。

OstreamOutputStream可以类似地在堆栈上分配,但它会堆分配一个您可能想要重用的缓冲区。如果您选择重用同一个对象,请确保在Flush()被销毁后调用CodedOutputStream来刷新缓冲区。

顺便说一下,OstreamOutputStream并不是特别有效,因为它必须在ostream已经在做的事情之上做自己的缓冲层。您可能希望序列化为字符串(str = message.SerializeAsString()message.SerializeToString(&str)),然后将其直接写入套接字(如果asio允许),因为它可能会避免冗余副本。