在C中,通过套接字发送包含枚举值的结构的最佳方式是什么

在C中,通过套接字发送包含枚举值的结构的最佳方式是什么,c,networking,enums,C,Networking,Enums,我有很多不同的结构包含枚举成员,我必须通过TCP/IP传输。虽然通信端点位于不同的操作系统(Windows XP和Linux)上,这意味着不同的编译器(gcc 4.x.x和MSVC 2008),但两个程序部分使用类型声明共享相同的头文件 出于性能方面的考虑,应该直接传输结构(参见下面的代码示例),而无需对内部的成员进行昂贵的序列化或流式传输 因此,问题是如何确保两个编译器对枚举成员使用相同的内部内存表示(即都使用32位无符号整数)。或者如果有更好的方法来解决这个问题 //type and enu

我有很多不同的结构包含枚举成员,我必须通过TCP/IP传输。虽然通信端点位于不同的操作系统(Windows XP和Linux)上,这意味着不同的编译器(gcc 4.x.x和MSVC 2008),但两个程序部分使用类型声明共享相同的头文件

出于性能方面的考虑,应该直接传输结构(参见下面的代码示例),而无需对内部的成员进行昂贵的序列化或流式传输

因此,问题是如何确保两个编译器对枚举成员使用相同的内部内存表示(即都使用32位无符号整数)。或者如果有更好的方法来解决这个问题

//type and enum declaration
typedef enum 
{
    A        = 1,
    B        = 2,
    C        = 3
} eParameter;


typedef enum
{
    READY    = 400,
    RUNNING  = 401,
    BLOCKED  = 402
    FINISHED = 403
} eState;


#pragma pack(push,1)
    typedef struct
    {
        eParameter mParameter;
        eState mState;
        int32_t miSomeValue;
        uint8_t miAnotherValue;
        ...
    } tStateMessage;
#pragma pack(pop)


//... send via socket
tStateMessage msg;
send(iSocketFD,(void*)(&msg),sizeof(tStateMessage));

//... receive message on the other side
tStateMessage msg_received;
recv(iSocketFD,(void*)(&msg_received),sizeof(tStateMessage));
另外

  • 由于两个端点都是小端点,因此端点性在这里不是问题
  • 而pack#pragma令人满意地解决了对齐问题
谢谢你的回答,
Axel

我会务实地回答你的问题,因为在权衡了性能提升和可能的负面影响后,你选择了一条相对危险的道路(至少我希望你选择了!)

如果还考虑了这些编译器的可移植性和抗未来更改的健壮性,那么经验方法将是预防问题的最佳方法

  • 确保在所有情况下都使用枚举的初始值设定项(您的示例会这样做)
  • 进行实证测试,以了解数据在接收端的解释情况
  • 记录两侧构建工具的版本号,并将其与源代码一起存档。最好也将工具存档
  • 记录您所做的一切,以便在将来进行不可预见的维护
  • 为最好的事祈祷!;-)
    我建议您使用专门为此类问题设计的序列化库之一,如:

    • ()
    • ()
    • ()
    您将获得的是最大的平台可移植性,一种更改接口和传输消息类型的简单方法,以及更多有用的功能


    注意,只有Avro有官方支持的C API。对于节省和协议缓冲区,你可以在C++ API上做一个薄薄的C包装,或者使用一个C API,比如./P>< P> <强>这是过早的优化。< /强>你没有做测量就做出了两个昂贵的假设。 第一个假设是,这部分代码首先是一个性能瓶颈。它是?不太可能。如果要对性能做出假设,那么安全的假设是网络速度将是瓶颈,而不是发送和接收网络消息的代码。仅此一点就可以阻止您考虑第二个假设

    第二个假设是,可移植地序列化结构将明显慢于编写结构的原始位。这种假设几乎总是错误的


    怀疑的?测量一下!:)

    强烈建议以某种方式序列化数据,或者至少使用有关硬件体系结构的指示符。即使使用相同的编译器,内部数据表示(小端、大端等)也可能会出现问题。

    如果不想进行序列化,我看到的一种方法是避免使用枚举,只使用32位无符号整数和#定义来模拟枚举。您牺牲了一些类型安全性来换取关于数据格式的一些保证

    否则,您将依赖于语言规范中未保证的行为在所有编译器上以相同的方式实现。如果您不担心通用的可移植性,只想确保在两个编译器上达到相同的效果,那么应该可以通过反复试验和大量测试来让这两个编译器做同样的事情。我相信C99规范允许枚举在内部为int或更小的大小,但不大于int。因此,我所看到的一件事情是,假设它向编译器提示了正确的方向:

    typedef enum
    {
        READY    = 400,
        RUNNING  = 401,
        BLOCKED  = 402,
        FINISHED = 403,
        MAX      = MAX_INT
    } eState;
    
    这将限制编译器对如何存储枚举的选择。请注意,编译器可能会违反该标准,但是,我知道gcc有一个非标准特性,如果需要,它将允许64位枚举

    此外,请查看:

    嗯,提示3和提示4的thx!你说得对,构建工具的版本号可能会变得便于维护!(我已经在处理1、2和5)您还需要对数据的大小进行标准化,并将其转换为该大小的整数进行传输。*交易类型安全性将是一种选择,但我不希望放弃。*和thx用于虚拟条目“MAX”创意!*是的,速度是这里的一个重要问题;我忘了在我的问题中提到实时环境…*但你是对的,我可能应该测量序列化开销。