簡體   English   中英

嘗試通過 ZeroMQ 發布-訂閱套接字 (C++) 發送和接收序列化 object 時出現 Cap'n Proto 未對齊數據錯誤

[英]Cap'n Proto unaligned data error while trying to send&receive serialized object over ZeroMQ pub-sub socket (C++)

我在我的 C++ 項目中使用 Cap'n Proto 和 ZeroMQ 進行進程間通信。 我有一個發送方和一個接收方,我正在通過 ZeroMQ 將 Cap'n Proto 消息從發送方發送到接收方。

當我嘗試在接收方接收和反序列化消息時出現以下錯誤:

*Bprepheading
terminate called after throwing an instance of 'kj::ExceptionImpl'
  what():  capnp/arena.c++:76: failed: expected reinterpret_cast<uintptr_t>(segment.begin()) % sizeof(void*) == 0 [2 == 0]; Detected unaligned data in Cap'n Proto message. Messages must be aligned to the architecture's word size. Yes, even on x86: Unaligned access is undefined behavior under the C/C++ language standard, and compilers can and do assume alignment for the purpose of optimizations. Unaligned access may lead to crashes or subtle corruption. For example, GCC will use SIMD instructions in optimizations, and those instrsuctions require alignment. If you really insist on taking your changes with unaligned data, compile the Cap'n Proto library with -DCAPNP_ALLOW_UNALIGNED to remove this check.
stack: 7fc92d717865 7fc92d7178b2 7fc92d734def 55d19f2b114c 55d19f2b0528 7fc92d347d09 55d19f2b0299
Aborted

我假設我需要在接收端正確對齊數據,但我不知道該怎么做。

我的 Sender.cpp 代碼

#include "../schema/message.capnp.h"
  
  #include <capnp/message.h>
  #include <capnp/serialize.h>
  #include <kj/std/iostream.h>
  #include <zmq.hpp>

int main() {
  
  // create a message builder
  capnp::MallocMessageBuilder message;
  Message::Builder messageBuilder = message.initRoot<Message>();

  // set the preparation and heading fields
  messageBuilder.setPreparation("prep");
  messageBuilder.setHeading("heading");

  zmq::context_t context(1);
  zmq::socket_t socket(context, ZMQ_PUB);

  socket.bind("tcp://*:5006");

  // serialize the message to a memory buffer
  kj::Array<capnp::word> serialized_message = capnp::messageToFlatArray(message);  
  
  while(true)
  {
    // create a ZeroMQ message from the serialized buffer
    zmq::message_t zmq_message(serialized_message.size() * sizeof(capnp::word));
    memcpy((void*)zmq_message.data(), serialized_message.begin(), serialized_message.size() * sizeof(capnp::word));
    socket.send(zmq_message);
  }

  return 0;
}

我的 Receiver.cpp 代碼


#include "../schema/message.capnp.h"

#include <kj/array.h>
#include <capnp/message.h>
#include <capnp/serialize.h>
#include <kj/std/iostream.h>
#include <zmq.hpp>

int main() {
  
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_SUB);

socket.connect("tcp://127.0.0.1:5006"); //or *
// socket.set(zmq::sockopt::subscribe, "");
socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);

zmq::message_t zmq_message;
socket.recv(zmq_message, zmq::recv_flags::none);
std::cout << zmq_message.to_string() << std::endl;

// create a memory buffer from the received message
kj::ArrayPtr<capnp::word> buffer(reinterpret_cast <capnp::word*>(zmq_message.data()), zmq_message.size() / sizeof(capnp::word));

// create an input stream from the memory buffer
capnp::FlatArrayMessageReader message_reader(buffer); 
Message::Reader message = message_reader.getRoot<Message>();

// print the preparation and heading fields
std::cout << "Preparation: " << message.getPreparation().cStr() << std::endl;
std::cout << "Heading: " << message.getHeading().cStr() << std::endl;

return 0;
}

我的 cap'n 原型模式語言 Message.capnp

@0xbf5147cbbecf40c1;

struct Message {
  preparation @0 :Text;
  heading @1 :Text;
}

不幸的是,來自 ZeroMQ 的緩沖區似乎沒有在字邊界上對齊。 因此,您必須復制一份。 您可以使用以下代碼來做到這一點:

auto buffer = kj::heapArray<capnp::word>(zmq_message.size() / sizeof(capnp::word));
memcpy(buffer.asBytes().begin(), zmq_message.data(), buffer.asBytes().size());

以這種方式失去 Cap'n Proto 的零拷貝優勢很煩人。 但是,memcpy 仍然比通常的解析快很多。 不幸的是,沒有辦法解決這個問題——盡管許多 CPU 可以接受未對齊的讀取,但 C++ 語言標准堅持認為它們是未定義的行為,因此編譯器可能會執行假設讀取對齊的優化。 例如,他們可能假設指針的低位為零。 所以事實證明你確實需要 alignment。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM