在 C/C++ 中实现通信协议

15

我正在着手开始在软件中实现一些专有通信协议栈,但不确定从何处开始。这是我以前从未做过的工作,我正在寻找资源方面的帮助,以获得最佳/推荐方法。

我将使用c / c ++,可以自由使用库(BSD / BOOST / Apache),但不使用GPL。我广泛使用了C ++,因此使用C ++的特性不是问题。

协议栈有三层,已经完全指定并经过正式验证。因此,我需要做的就是在指定的语言中完全实现和测试它。还应该提到的是,协议非常简单,但可以在可靠的物理传输层上运行在不同的设备上。我了解协议状态机的事件,输入,输出,副作用和行为。通常,接收到中断以读取从物理层接收到的消息并将其发送到等待设备。接收设备可以处理并将响应消息传递到协议层以通过物理层发送出去。

任何关于参考文献/建议的帮助都将不胜感激。如果只是为了帮助我了解如何实现它们,我愿意使用其他语言,但我最终必须使用选择的语言。

更新:我希望实现的示例协议类似于SNEP

我不需要担心连接管理。我们可以假设连接已经建立,而协议所做的就是数据交换,其中协议消息已在规范中定义良好。


2
这个问题太泛泛了,开始设计和实现它,并在遇到具体问题时寻求帮助。 - peoro
1
这是一个有趣的任务。在设计实现时,请记住您希望使用单元测试独立测试每个层。为了获得更具体的帮助,请尝试指定您从哪里开始(调整连接到PIO的引脚?)以及您想要获得什么样的帮助。 - harper
我同意@peoro的观点。此外,我不确定 - 你是在哪里开发这个?在任何正常的协议(通过以太网)中,操作系统处理网络堆栈的较低层。您可以使用操作系统API构建您所谓的协议层。但是,如果不知道您打算在哪里构建此协议以及具体存在什么问题,我无法提供真正的帮助。 - user257111
@Ninefingers 我需要实现它,因为我正在实现一个供应用程序使用的 FW 组件。根据您所说的内容,我基本上正在实现您所谓的“操作系统API”。 - dubnde
@peoro 是的,我知道更具体的信息会帮助我更好地开始,但这是我尝试获取尽可能多的信息来帮助我入手的方式。你提出的建议已经非常有帮助了,所以在从其他建议中获得一些想法后,我会遵循你的建议。 - dubnde
3个回答

11

从接口和消息开始。

声明会话的接口,允许对等体交换消息。将消息声明为 C++ 结构体,并使用简单类型,例如 ints、doubles、std::string 和 std::vector。例如:

// these are your protocol messages
struct HelloRequest {
    uint32_t seq_no;
    // more stuff
};
struct HelloResponse {
    uint32_t seq_no;
    // more stuff
};

// Session callback for received messages
struct SessionReceiver {
    virtual void connected(Session*) = 0;
    virtual void receive(Session* from, HelloRequest msg) = 0;
    virtual void receive(Session* from, HelloResponse msg) = 0;
    virtual void disconnected(Session*) = 0;
};

// Session interface to send messages
struct Session {
    virtual void send(HelloRequest msg) = 0;
    virtual void send(HelloResponse msg) = 0;
};

// this connects asynchronously and then calls SessionReceiver::connected() with a newly established session
struct SessionInitiator {
    virtual void connect(SessionReceiver* cb, std::string peer) = 0;
};

// this accepts connections asynchronously and then calls SessionReceiver::connected() with a newly accepted session
struct SessionAcceptor {
    virtual void listen(SessionReceiver* cb, std::string port) = 0;
};

通过编写使用这些接口的业务逻辑,测试你的接口。一旦你有信心这些接口能够允许你实现所需的逻辑,就使用你喜欢的事件驱动框架(如libevent或Boost.Asio)实现接口和消息的序列化。

编辑:请注意,接口使您可以拥有模拟或测试实现。另外,序列化发生在接口后面意味着对于进程内同级,您不必对消息进行序列化和反序列化,可以原样传递。


我喜欢这个建议。我会探索它,并在出现任何具体问题时回来。 - dubnde
使用 struct 作为在机器之间发送的消息的可靠解决方案吗?据我所知,编译器不需要按照作者通常假定的方式打包结构体 -- 可能会插入填充以提高性能等,至少在没有使用编译器指令来指示它们的情况下。当远程主机接收数据并尝试将其再次读入结构体时,不能保证相同结构体的布局在那里是相同的。我说的对吗? - Armen Michaeli
1
@amn 这些消息必须进行序列化和反序列化,即它们不是线路表示。您可以使用Google协议缓冲区或其他任何工具来完成此操作。 - Maxim Egorushkin

5

Boost.ASIO在C++中涉及异步(或同步)网络通信方面非常前沿。


5
请看一下Google Protocol Buffers
根据描述:
协议缓冲区是一种灵活,高效,自动化的序列化结构化数据机制 - 类似于XML,但更小,更快,更简单。您只需定义一次要结构化的数据,然后就可以使用特殊生成的源代码轻松地将结构化数据写入和读取到各种数据流中,并使用各种语言。您甚至可以更新数据结构,而不会破坏针对“旧”格式编译的已部署程序。
协议缓冲区是语言和平台中立的,因此应适合您的项目。我找不到许可证,但至少我没有找到任何地方写着“GPL”。
这将帮助您处理协议。对于实际的数据传输,除非您自己编写操作系统,否则应该使用一些基本原语。如果您提供更多细节,才能给出更确切的实现帮助。例如,您在使用什么通信渠道?以太网?
但一般而言,您应该使ISR尽可能短。在这些解决方案中,这通常意味着将数据复制到环形缓冲区中。这样,您就不必在ISR中分配内存。ISR在复制数据后,应通知上层软件包。如果可以使用DMA,请使用它。在这种情况下,甚至可以在开始DMA传输之前发送通知。
您还可以查看Linux设备驱动程序,特别是第10章。请查看有关底部和顶部的部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接