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

发布于 2024-09-06 01:29:36 字数 1070 浏览 5 评论 0原文

我有很多不同的结构体,其中包含必须通过 TCP/IP 传输的枚举成员。虽然通信端点位于不同的操作系统(Windows XP 和 Linux)上,这意味着不同的编译器(gcc 4.xx 和 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));

另外...

  • 由于两个端点都是小端机器,所以这里的字节顺序不是问题。
  • #pragma 包很好地解决了对齐问题。

谢谢你的回答, 阿克塞尔

I've lots of different structs containing enum members that I have to transmit via TCP/IP. While the communication endpoints are on different operating systems (Windows XP and Linux) meaning different compilers (gcc 4.x.x and MSVC 2008) both program parts share the same header files with type declarations.

For performance reasons, the structures should be transmitted directly (see code sample below) without expensively serializing or streaming the members inside.

So the question is how to ensure that both compilers use the same internal memory representation for the enumeration members (i.e. both use 32-bit unsigned integers). Or if there is a better way to solve this problem...

//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));

Additionally...

  • Since both endpoints are little endian maschines, endianess is not a problem here.
  • And the pack #pragma solves alignment issues satisfactorily.

Thx for your answers,
Axel

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

离不开的别离 2024-09-13 01:29:36

我会务实地回答你的问题,因为在权衡性能收益与可能的缺点之后,你选择了一条相对危险的路径(至少我希望你这样做!)。

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

  1. 确保在所有情况下都使用枚举的初始值设定项(您的示例执行此操作)。
  2. 进行实证测试,看看接收方如何解释数据。
  3. 记录双方构建工具的版本号,并与源代码一起归档。最好也归档工具。
  4. 记录您所做的一切,以便将来的不可预见的维护不会受到阻碍。
  5. 祈求最好的结果! ;-)

I'll answer your question pragmatically because you've chosen a relatively risky path after weighing the performance gains against the possible downsides (at least I hope you have!).

If portability and robustness against future changes to those compilers have also been considered then an empirical approach would be the best guard against problems.

  1. Ensure you are using initializers for the enums (your examples do this) in all cases.
  2. Do empirical testing to see how the data is interpreted on the receiving side.
  3. Record the version numbers of the build tools on both sides and archive them with the source code. Preferably archive the tools as well.
  4. Document everything you did so unforeseen maintenance in the future is not handicapped.
  5. Pray for the best! ;-)
德意的啸 2024-09-13 01:29:36

我建议您使用专为此类问题设计的序列化库之一,例如:

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

请注意,只有 Avro 具有官方支持的 C API。对于 Thrift 和 Protocol Buffers,您可以在 C++ API 上制作一个精简的 C 包装器,或者使用 C API 之一,例如 protobuf-c

I would advise you to use one of the serialization libraries specially designed for such problems, like:

What you will get is maximum platform portability, an easy way of changing the interface and the type of messages transmitted plus a lot more useful features.

Note that only Avro has an officially supported C API. For Thrift and Protocol Buffers you either make a thin C wrapper over the C++ API or use one of the C APIs, like protobuf-c.

白色秋天 2024-09-13 01:29:36

这是不成熟的优化。您在没有测量的情况下做出了两个代价高昂的假设。

第一个假设是这部分代码首先是性能瓶颈。是吗?不太可能。如果要对性能做出假设,那么安全的假设是网络速度将成为瓶颈,而不是发送和接收网络消息的代码。仅此一点就应该阻止您考虑第二个假设。

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

持怀疑态度?测量一下! :)

This is premature optimization. You have made two costly assumptions without measurements.

The first assumption is that this part of the code is a performance bottleneck in the first place. Is it? Very unlikely. If one is going to make assumptions about performance, then the safe assumption is that the network speed will be the bottleneck, not the code which sends and receives the network messages. This alone should prevent you from ever considering the second assumption.

The second assumption is that serializing the struct portably will be noticeably slower than writing the raw bits of the struct. This assumption is nearly always false.

Skeptical? Measure it! :)

空城缀染半城烟沙 2024-09-13 01:29:36

如果您不想进行序列化,我见过的一种方法是避免使用枚举,而简单地使用 32 位无符号整数和 #DEFINE 来模拟枚举。您用一些类型安全性来换取一些数据格式的保证。

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

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

这应该限制编译器对如何存储枚举的选择。请注意,编译器可能违反标准,但是,我知道 gcc 有一个非标准功能,如果需要,它将允许 64 位枚举。

另外,请查看:
C 中枚举的大小是多少?

If you don't want to go through serialization, one method I've seen used is to eschew enums and simply use 32-bit unsigned ints and #DEFINEs to emulate enums. You trade away some type safety for some assurances about data format.

Otherwise, you are relying on behaviour that isn't guarenteed in the language specification to be implemented the same way on all your compilers. If you aren't worried about general portability and just want to ensure the same effect on two compilers, it should be possible through trial and error and a lot of testing to get the two to do the same thing. I believe the C99 spec allows enums to internally be the size of int or smaller, but not larger than int. So one thing I've seen done to supposedly hint the compiler in the right direction is:

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

This should limit the compiler's choices for how to store the enum. Note that compilers can violate the standard, however, I know gcc has a non-standard feature where it will allow 64-bit enums if necessary.

Also, check out:
What is the size of an enum in C?

柠檬色的秋千 2024-09-13 01:29:36

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

It is strongly recommended to serialize the data in some way or at least use an indicator about the hardware architecture. Even if you use the same compiler, you can have problems with internal data representations (little endian, big endian etc).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文