c ++通过网络发送结构

时间:2016-12-26 12:44:49

标签: c++ structure boost-asio

我正在使用具有预定义结构的英特尔SGX。我需要通过使用boost::asio操作的网络连接发送这些结构。 需要发送的结构具有以下格式:

typedef struct _ra_samp_request_header_t{
    uint8_t  type;     /* set to one of ra_msg_type_t*/
    uint32_t size;     /*size of request body*/
    uint8_t  align[3];
    uint8_t body[];
} ra_samp_request_header_t;

对于发送和接收,使用方法async_writeasync_async_read_some

boost::asio::async_write(socket_, boost::asio::buffer(data_, max_length),
                                          boost::bind(&Session::handle_write, this,
                                          boost::asio::placeholders::error));

socket_.async_read_some(boost::asio::buffer(data_, max_length),
                            boost::bind(&Session::handle_read, this,
                            boost::asio::placeholders::error,
                            boost::asio::placeholders::bytes_transferred));

data_定义为

enum { max_length = 1024 };
char data_[max_length];

我的第一种方法是将单个结构元素转换为字符串并将它们存储在vector<string>中,然后进一步转换为char*,而每个元素由\n分隔。

但是当接收方将收到的char*组装回原始结构时,我遇到了一些麻烦。

这真的是应该这样做的方式还是有更好的方式来转移结构

1 个答案:

答案 0 :(得分:1)

你需要便携式吗?

如果不是:

  1. 简单化方法
  2. 使用Boost序列化
  3. 如果需要便携式

    1. 使用ntohlhtonl调用等
    2. 简化了简单方法
    3. 使用EOS Portable Archives
    4. 升级序列化

      1。简单化的方法

      只需将结构作为POD数据发送(假设它实际上是POD,假设您的问题中的代码是公平的假设,因为结构显然不是C ++)。

      在2个线程(侦听器和客户端)上使用同步调用的简单示例显示了服务器如何将数据包发送到客户端正确接收的客户端。

      注意:

      • 使用异步调用是一项微不足道的更改(将writeread更改为async_writeasync_write,除非使用协同程序,否则只会使控制流更不清晰)< / LI>
      • 我展示了如何在C ++ 11中以(例外)安全方式使用malloc / free。您可能希望在代码库中创建一个简单的Rule-Of-Zero包装器。

      <强> Live On Coliru

      #include <boost/asio.hpp>
      #include <cstring>
      
      namespace ba = boost::asio;
      using ba::ip::tcp;
      
      typedef struct _ra_samp_request_header_t{
          uint8_t  type;     /* set to one of ra_msg_type_t*/
          uint32_t size;     /*size of request body*/
          uint8_t  align[3];
          uint8_t  body[];
      } ra_samp_request_header_t;
      
      #include <iostream>
      #include <thread>
      #include <memory>
      
      int main() {
          auto unique_ra_header = [](uint32_t body_size) {
              static_assert(std::is_pod<ra_samp_request_header_t>(), "not pod");
      
              auto* raw = static_cast<ra_samp_request_header_t*>(::malloc(sizeof(ra_samp_request_header_t)+body_size));
              new (raw) ra_samp_request_header_t { 2, body_size, {0} };
              return std::unique_ptr<ra_samp_request_header_t, decltype(&std::free)>(raw, std::free);
          };
      
          auto const& body = "There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.";
      
          auto sample = unique_ra_header(sizeof(body));
          std::strncpy(reinterpret_cast<char*>(+sample->body), body, sizeof(body));
      
          ba::io_service svc;
          ra_samp_request_header_t const& packet = *sample;
          auto listener = std::thread([&] {
              try {
                  tcp::acceptor a(svc, tcp::endpoint { {}, 6767 });
                  tcp::socket s(svc);
                  a.accept(s);
      
                  std::cout << "listener: Accepted: " << s.remote_endpoint() << "\n";
                  auto written = ba::write(s, ba::buffer(&packet, sizeof(packet) + packet.size));
                  std::cout << "listener: Written: " << written << "\n";
              } catch(std::exception const& e) {
                  std::cerr << "listener: " << e.what() << "\n";
              }
          });
      
          std::this_thread::sleep_for(std::chrono::milliseconds(10)); // make sure listener is ready
      
          auto client = std::thread([&] {
              try {
                  tcp::socket s(svc);
                  s.connect(tcp::endpoint { {}, 6767 });
      
                  // this is to avoid the output to get intermingled, only
                  std::this_thread::sleep_for(std::chrono::milliseconds(200));
      
                  std::cout << "client: Connected: " << s.remote_endpoint() << "\n";
      
                  enum { max_length = 1024 };
                  auto packet_p = unique_ra_header(max_length); // slight over allocation for simplicity
                  boost::system::error_code ec;
                  auto received = ba::read(s, ba::buffer(packet_p.get(), max_length), ec); 
      
                  // we expect only eof since the message received is likely not max_length
                  if (ec != ba::error::eof) ba::detail::throw_error(ec);
      
                  std::cout << "client: Received: " << received << "\n";
                  (std::cout << "client: Payload: ").write(reinterpret_cast<char const*>(packet_p->body), packet_p->size) << "\n";
              } catch(std::exception const& e) {
                  std::cerr << "client: " << e.what() << "\n";
              }
          });
      
          client.join();
          listener.join();
      }
      

      打印

      g++ -std=gnu++11 -Os -Wall -pedantic main.cpp -pthread -lboost_system && ./a.out
      listener: Accepted: 127.0.0.1:42914
      listener: Written: 645
      client: Connected: 127.0.0.1:6767
      client: Received: 645
      client: Payload: There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.
      

      1b中。使用包装器简单化

      因为对于Boost序列化,无论如何都要方便地使用这样的包装器,让我们使用这样的“Zero Of Zero”包装器重写它:

      <强> Live On Coliru

      namespace mywrappers {
          struct ra_samp_request_header {
              enum { max_length = 1024 };
      
              // Rule Of Zero - https://rmf.io/cxx11/rule-of-zero
              ra_samp_request_header(uint32_t body_size = max_length) : _p(create(body_size)) {}
      
              ::ra_samp_request_header_t const& get() const { return *_p; };
              ::ra_samp_request_header_t&       get()       { return *_p; };
      
            private:
              static_assert(std::is_pod<::ra_samp_request_header_t>(), "not pod");
              using Ptr = std::unique_ptr<::ra_samp_request_header_t, decltype(&std::free)>;
              Ptr _p;
      
              static Ptr create(uint32_t body_size) {
                  auto* raw = static_cast<::ra_samp_request_header_t*>(::malloc(sizeof(::ra_samp_request_header_t)+body_size));
                  new (raw) ::ra_samp_request_header_t { 2, body_size, {0} };
                  return Ptr(raw, std::free);
              };
          };
      }
      

      2。使用Boost序列化

      没有太多的麻烦,这里有一个简单的方法来实现该包装器的类内序列化:

      friend class boost::serialization::access;
      
      template<typename Ar>
      void save(Ar& ar, unsigned /*version*/) const {
          ar & _p->type
             & _p->size
             & boost::serialization::make_array(_p->body, _p->size);
      }
      
      template<typename Ar>
      void load(Ar& ar, unsigned /*version*/) {
          uint8_t  type = 0;
          uint32_t size = 0;
          ar & type & size;
      
          auto tmp = create(size);
          *tmp = ::ra_samp_request_header_t { type, size, {0} };
      
          ar & boost::serialization::make_array(tmp->body, tmp->size);
      
          // if no exceptions, swap it out
          _p = std::move(tmp);
      }
      
      BOOST_SERIALIZATION_SPLIT_MEMBER()
      

      然后使用streambuf

      将测试驱动程序简化为此
      auto listener = std::thread([&] {
          try {
              tcp::acceptor a(svc, tcp::endpoint { {}, 6767 });
              tcp::socket s(svc);
              a.accept(s);
      
              std::cout << "listener: Accepted: " << s.remote_endpoint() << "\n";
      
              ba::streambuf sb;
              {
                  std::ostream os(&sb);
                  boost::archive::binary_oarchive oa(os);
                  oa << sample;
              }
      
              auto written = ba::write(s, sb);
              std::cout << "listener: Written: " << written << "\n";
          } catch(std::exception const& e) {
              std::cerr << "listener: " << e.what() << "\n";
          }
      });
      
      std::this_thread::sleep_for(std::chrono::milliseconds(10)); // make sure listener is ready
      
      auto client = std::thread([&] {
          try {
              tcp::socket s(svc);
              s.connect(tcp::endpoint { {}, 6767 });
      
              // this is to avoid the output to get intermingled, only
              std::this_thread::sleep_for(std::chrono::milliseconds(200));
      
              std::cout << "client: Connected: " << s.remote_endpoint() << "\n";
      
              mywrappers::ra_samp_request_header packet;
              boost::system::error_code ec;
      
              ba::streambuf sb;
              auto received = ba::read(s, sb, ec); 
      
              // we expect only eof since the message received is likely not max_length
              if (ec != ba::error::eof) ba::detail::throw_error(ec);
      
              std::cout << "client: Received: " << received << "\n";
      
              {
                  std::istream is(&sb);
                  boost::archive::binary_iarchive ia(is);
                  ia >> packet;
              }
      
              (std::cout << "client: Payload: ").write(reinterpret_cast<char const*>(packet.get().body), packet.get().size) << "\n";
          } catch(std::exception const& e) {
              std::cerr << "client: " << e.what() << "\n";
          }
      });
      

      所有其他代码与上述内容保持不变,请参阅 Live On Coliru 。输出没有变化,除了使用Boost 1.62在我的64位机器上数据包大小增加到683。

      3。使简化方法复杂化

      我没心情去演示这个。感觉就像是C程序员而不是C ++程序员。当然,有一些聪明的方法可以避免编写字符串等等。对于现代方法,请参见例如。

      4。使用EAS Portable Archive

      使用代码3进行简单的插入练习。