【问题标题】:Should I use #defines or enums for commands over network?我应该使用#defines 还是枚举来通过网络执行命令?
【发布时间】:2009-07-12 00:30:21
【问题描述】:

我必须通过网络传输一些值以用作命令,并且我希望使其尽可能高效和稳健,即我需要意见来了解这些命令的用途, #defines 还是枚举?

命令的范围不应超过 20 个命令(假设每个定义的命令响应为 40 个),因此根据我所听到的大多数情况,它非常适合 char 限制。

到目前为止,我假设最好的方法是使用枚举。

【问题讨论】:

    标签: c enums network-programming performance network-protocols


    【解决方案1】:

    在互联网上传输数值时,需要注意两点。

    第一个是字节序,即字节(或有时位)出现的顺序。假设您有 32 位值 0xAABBCCDD。 Intel CPU 是 little-endian 机器,这意味着这些字节将存储为 { 0xDD, 0xCC, 0xBB, 0xAA }。换句话说,最低有效字节存储在最低地址。在大端机器中,字节将被存储为 { 0xAA, 0xBB, 0xCC, 0xDD },最低有效字节在最高地址。

    在两台机器之间传输多字节整数时,您必须确保它们都正确解释彼此的数据,即使它们具有不同的字节顺序。值得庆幸的是,有一个称为网络字节顺序的标准,它是大端的,并且有 4 个有用的函数可以在主机顺序和网络顺序之间进行转换:

    ntohl(网络到主机,长)
    ntohs(网络到主机,简称)
    htonl(主机到网络,长)
    htons(主机到网络,简称)

    长版本适用于 32 位整数,短版本适用于 16 位整数。只要你总是在通过网络传输数据之前调用 *hton** 并在从网络读取时调用 *ntoh** ,数据就会以正确的字节顺序排列。

    当然,解决这个问题的最简单方法,尤其是因为您只有 20 个命令,是只使用单个字节,或 chars。

    您必须处理的第二个问题是编码。有符号整数如何表示?使用符号位?二进制补码?同样,当网络上的不同平台使用不同的表示时,您会遇到问题。如果你坚持使用无符号类型,你应该不会有问题。

    您在程序中使用什么来表示您的命令完全取决于您。只需确保您定义好协议并在传输和读取数据时遵守该协议即可。

    例如:

    enum command_t { one, two, three }
    
    void send_command(command_t c) {
        send((unsigned char)c);
    }
    
    command_t read_command() {
        return (command_t)recv();
    }
    
    void send(unsigned char c) { ... }
    unsigned char recv() { ... }
    

    【讨论】:

    • 我认为在这种情况下,将每个命令存储在 unsigned char 变量中并将其等同于一个值,我将它存储在 #define 中,就像它一直所做的那样。我想现在我明白为什么#define 一直被用于错误代码、状态名称、命令......等等。
    【解决方案2】:

    从效率的角度来看,它没有任何区别。 #define 或 enum 只是分配数值的一种方式……真正的诀窍是如何将该值打包到网络上。

    也就是说,我同意我在这里看到的其他答案,而且我认为您无论如何都在担心错误的事情。设计网络协议需要深思熟虑,使其在许多系统中可移植和可用通常应该优先考虑效率问题(当然,该规则也有例外)。

    【讨论】:

      【解决方案3】:

      我还建议使用基于行分隔的文本协议。除了不必担心enum-vs.#define 值之外,还有其他一些好处:

      黑客/测试

      为基于文本的协议手动生成一组命令非常容易。考虑在 HTTP 服务器上测试错误条件:

      $ telnet www.yahoo.com 80
      Trying 69.147.76.15...
      Connected to www-real.wa1.b.yahoo.com.
      Escape character is '^]'.
      GOAT / HTTP/1.1
      

      我不需要任何花哨的东西来理解服务器是如何响应的:

      HTTP/1.1 400 Bad Request
      Date: Sat, 11 Jul 2009 16:20:59 GMT
      Cache-Control: private
      Connection: close
      Transfer-Encoding: chunked
      Content-Type: text/html; charset=iso-8859-1
      

      这对于调试来说非常棒。您可以创建一组测试脚本来输入telnet/nc,它们将比任何二进制协议测试脚本更容易维护。

      便携性

      如果您根据 ASCII 文本(或者可能是 UTF-8)来定义您的协议,那么您永远不必担心新平台上的低级别问题。处理二进制协议时,字节序、结构对齐和字长都是问题。 ASCII 可能需要额外的序列化/反序列化步骤,但在网络协议中这通常是必需的。

      分享

      如果您想与其他人一起开展项目,能够将网络操作描述为纯文本是非常棒的。如果其他人想开发自己的客户端,则不需要他们使用您的头文件或任何东西,他们只需要遵守类似 RFC 的标准。这对于宠物项目没有用处,但对于任何可能变得更大的项目来说,这是一件好事。

      【讨论】:

        【解决方案4】:

        你想要意见吗?我的意见是您应该使用基于文本的协议,其中每个命令都是一个关键字。是否以及如何在程序中将它们转换为枚举取决于您,但在网络级别上,使用基于文本的命令意味着您的协议更具可扩展性。

        许多现实生活中的协议都是基于文本的,我敢说它们的工作效率很高。例如,HTTP、FTP、SMTP 等都是基于文本的。

        【讨论】:

        • HTTP 和 SMTP 对于文本以外的任何东西都不是很有效,而且 FTP 的实际数据传输是二进制的。这些现实生活中的文本协议之所以好,是因为它们很简单,而不是因为它们“非常高效”。
        猜你喜欢
        • 2023-03-16
        • 2010-12-03
        • 1970-01-01
        • 2011-09-07
        • 1970-01-01
        • 2013-06-09
        • 1970-01-01
        • 2021-05-22
        • 2012-11-09
        相关资源
        最近更新 更多