使用 Cap'n'Proto 进行序列化时进行流式传输
Stream while serializing with Cap'n'Proto
考虑像这样的Cap'n'Proto模式:
struct Document {
header @0 : Header;
records @1 :List(Record); // usually large number of records.
footer @2 :Footer;
}
struct Header { numberOfRecords : UInt32; /* some fields */ };
struct Footer { /* some fields */ };
struct Record {
type : UInt32;
desc : Text;
/* some more fields, relatively large in total */
}
现在我想序列化(即构建)一个文档实例并将其流式传输到远程目标。
由于文档通常非常大,我不想在发送之前将其完全构建在内存中。相反,我正在寻找一个通过电线直接逐个结构发送结构的构建器。使得额外的需要的内存缓冲区是恒定的(即O(max(sizeof(Header),sizeof(Record),sizeof(Footer)))。
查看教程材料,我找不到这样的构建器。MallocMessageBuilder
似乎首先在内存中创建所有内容(然后您调用writeMessageToFd
)。
Cap'n'Proto API是否支持这样的用例?
还是Cap'n'Proto更适合用于在发送之前放入内存的消息?
在此示例中,可以省略 Document 结构,然后可以只发送一个标头消息、n 个记录消息和一个页脚的序列。由于 Cap'n'Proto 消息是自定性的,因此这应该有效。但是你失去了你的文档根 - 也许有时这不是一个真正的选择。
您概述的解决方案 - 将文档的各个部分作为单独的消息发送 - 可能最适合您的用例。从根本上说,Cap'n Proto 不是为流式传输单个消息的块而设计的,因为这不适合其随机访问属性(例如,当您尝试跟随指向您尚未收到的块的指针时会发生什么?相反,当您想要流式传输时,您应该将大消息拆分为一系列较小的消息。
也就是说,与其他类似的系统(例如Protobuf)不同,Cap'n Proto并不严格要求消息适合内存。具体来说,您可以使用 mmap(2)
.如果文档数据来自磁盘上的文件,则可以将该文件mmap()
到内存中,然后将其合并到邮件中。使用mmap()
,在您尝试访问内存之前,操作系统实际上不会从磁盘读取数据,并且操作系统也可以在访问页面后从内存中清除页面,因为它知道磁盘上仍有副本。这通常可以让您编写更简单的代码,因为您不再需要考虑内存管理。
为了将 mmap()
ed 块合并到 Cap'n Proto 消息中,您需要使用 capnp::Orphanage::referenceExternalData()
。例如,给定:
struct MyDocument {
body @0 :Data;
# (other fields)
}
你可以这样写:
// Map file into memory.
void* ptr = (kj::byte*)mmap(
nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
KJ_FAIL_SYSCALL("mmap", errno);
}
auto data = capnp::Data::Reader((kj::byte*)ptr, size);
// Incorporate it into a message.
capnp::MallocMessageBuilder message;
auto root = message.getRoot<MyDocument>();
root.adoptDocumentBody(
message.getOrphanage().referenceExternalData(data));
由于 Cap'n Proto 是零拷贝的,因此它最终会将 mmap()
ed 内存直接写入插槽,而无需访问它。然后由操作系统根据需要从磁盘读取内容并读出到套接字。
当然,您在接收端仍然有问题。您会发现设计接收端以读取mmap()
内存要困难得多。一种策略可能是先将整个流直接转储到文件(不涉及 Cap'n Proto 库),然后mmap()
该文件并使用capnp::FlatArrayMessageReader
就地读取mmap()
ed 数据。
我描述所有这些是因为这是一件简洁的事情,可以使用Cap'n Proto,但不能使用大多数其他序列化框架(例如,您无法使用Protobuf执行此操作)。玩弄mmap()
的把戏有时真的很有用——我已经在Cap'n Proto的父项目Sandstorm的几个地方成功地使用了它。但是,我怀疑对于您的用例,将文档拆分为一系列消息可能更有意义。
- 如何使用CMake编译.proto文件来生成.grpcp.pb.cc和.grpc.pb.h文件
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- Angelscript从C++传输数组
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- 从服务器传输到客户端的消息不会出现
- USB传输的LibUSB C++格式不同
- 在本地网络中通过OpenCV(C++)实时流式传输图像
- 将相机数据从服务器实时流式传输到客户端
- 加密在 Windows、C++ 和 Java 中传输中的数据
- 如何从网站获取数据并将其传输到数据库?
- 使用蓝牙组件将数据从Android手机传输到串行设备时出现问题
- 在 OpenCV 中应用公式并传输图像
- 在不传输的情况下更改 Win32 中的串行端口波特率
- Vulkan 的传输队列系列功能和显卡支持:条件检查是否准确?
- 如何使用CAPL的诊断功能获取CAN传输的数据(256字节)?
- 如何通过ROS将realsense数据传输到其他设备
- 将音频从浏览器流式传输到WebRTC本机C++应用程序
- 将FFMpeg AVFrame对象从C++应用程序流式传输到Python的最佳方法?
- 使用IMFSourceReader(Microsoft媒体基金会)进行音频流传输
- 使用 Cap'n'Proto 进行序列化时进行流式传输