【问题标题】:Converting a structure containing floats into something more network friendly将包含浮点数的结构转换为对网络更友好的结构
【发布时间】:2019-12-04 18:51:55
【问题描述】:

我正在尝试通过网络发送一个包含浮点数据的结构,该结构由两种不同的硬件架构组成,用 C 语言编写。

客户端运行在x86_64架构上,服务器运行在PPC(32位)架构上。

似乎我将数据转换为网络友好格式和从网络友好格式转换的唯一选择是:

htons/ntohs(2 字节)和htonl/ntohl(4 字节)

似乎没有处理浮点数的版本(这是有道理的,因为不同的架构/字节序有不同的浮点数表示)。

因此,我尝试通过以下方式将浮点数拆分为整数和指数格式:

void FtoME(float num, int32_t* mantissa, int32_t* exponent)
{
    int32_t sig = (int32_t)num;
    int32_t exp = 0;
    double frac = num-sig;
    double temp = num;
    while (frac > 0 && exp > -20)
    {
        temp = temp * 10; //left shift the whole number
        sig = (int32_t)temp; //get the integer part
        frac = temp - sig; //get the fractional part
        exp--;
    }
    printf("Scientific note: %dx10^%d\n",sig, exp);
    *mantissa = sig;
    *exponent = exp;
}

现在,虽然这在理论上确实有效,但在实践中,我遇到了很多溢出,所以很明显,这不是处理这个问题的正确方法。

是否有其他方法可以避免溢出,并将浮点数转换为网络友好格式(重要的是,再次返回),同时不丢失任何数据?

【问题讨论】:

  • 唯一几乎通用的数据传输方式是纯 UTF-8 编码文本。任何形式的二进制数据总会有特殊或极端的情况,在这些情况下,要在所有现在生活的平台和系统中以可移植的方式正确处理它是非常困难的。
  • 定义传输协议并坚持下去。如果您的传输格式是“big endian 32-bit IEEE 754”,那么各方都必须对此达成一致。如果您计划支持奇怪的架构,您可能需要使用预处理器定义,但通常您可以期望只担心字节顺序,即this answer to a very similar question
  • herehere 讨论这些问题。请注意,如果您发明另一种自己的设计,情况可能会更糟。通过这样做,您可以确保您永远具有兼容性。
  • “当您有设计复杂二进制文件格式或复杂二进制应用程序协议的冲动时,通常最好躺下直到感觉过去”——Eric Steven Raymond 在The Art of Unix Programming, chapter 5

标签: c network-programming computer-science


【解决方案1】:

IEEE 754 标准对于大多数架构来说应该足够了——而且只有那些不兼容的架构,你只需要担心转换到 IEEE 754 并再次转换回来。对于字节顺序的东西,给定一个 32 位浮点数,您可以使用 uint32_t tmp; memcpy(&tmp, &f, sizeof(f));htonlntohl

【讨论】:

    【解决方案2】:

    似乎没有处理浮点数的版本(这是有道理的,因为不同的架构/字节序有不同的浮点数表示)。

    不,它没有“意义”:每个大于一个字节的数据类型都会受到系统字节序的影响。这包括 shortlong 整数。

    htonlntohl 函数正是您要寻找的,也可用于发送 32 位浮点数(但不是 doubles,它们是 64 位...仍然可行,但有点复杂)。

    htonl 函数将 32 位值从主机字节序转换为网络字节序(即大字节序),而ntohl 函数将 32 位值从网络字节序转换为主机字节序。您所要做的就是在发送和接收时将您的float 适当地转换为uint32_t,然后您就可以开始了。

    服务器:

    float f = 10e-9;
    uint32_t tmp;
    
    memcpy(&tmp, &f, 4);
    uint32_t data = htonl(tmp);
    
    send_to_client(data);
    
    // Or if you are sending as raw bytes:
    send_to_client((char*)&data, 4);
    

    客户:

    uint32_t data;
    
    data = receive_from_server();
    
    // Or if you are receiving as raw bytes:
    char *bytes = receive_from_server(4);
    memcpy(&data, bytes, 4);
    
    float f = (float)ntohl(data);
    

    作为处理struct 的示例,假设您想通过网络发送此struct

    struct data {
        char name[10];
        uint32_t something1;
        uint16_t something2;
        float value1;
        float value2;
    };
    

    那么您可以执行以下操作。

    服务器:

    struct data x;
    // Do something to initialize the fields of x.
    
    char buffer[24]; // 10 + 4 + 2 + 4 + 4
    uint32_t tmpl;
    uint16_t tmps;
    
    memcpy(buffer, x.name, 10);
    
    tmpl = htonl(x.something1);
    memcpy(buffer + 10, &tmpl, 4);
    
    tmps = htons(x.something2);
    memcpy(buffer + 14, &tmps, 2);
    
    memcpy(&tmpl, &x.value1, 4)
    tmpl = htonl(tmpl);
    memcpy(buffer + 16, &tmpl, 4);
    
    memcpy(&tmpl, &x.value, 4)
    tmpl = htonl(tmpl);
    memcpy(buffer + 20, &tmpl, 4);
    
    send_to_client(buffer, 24);
    

    客户:

    char *buffer = receive_from_server(24);
    
    struct data x;
    uint32_t tmpl;
    uint16_t tmps;
    
    memcpy(x.name, buffer, 10);
    
    memcpy(&tmpl, buffer + 10, 4);
    x.something1 = ntohl(tmpl);
    
    memcpy(&tmps, buffer + 14, 2);
    x.something2 = ntohl(tmps);
    
    memcpy(&tmpl, buffer + 16, 4);
    tmpl = ntohl(tmpl);
    memcpy(&x.value1, &tmpl, 4);
    
    memcpy(&tmpl, buffer + 20, 4);
    tmpl = ntohl(tmpl);
    memcpy(&x.value2, &tmpl, 4);
    

    【讨论】:

    • 这非常适合发送单个值,但如果我必须发送几个值或值的结构怎么办?
    • @IanYoung 那么你必须为每个值都这样做。例如,您可以只使用循环。打包结构也一样,只需将每个字段转换为网络字节序即可。如果你愿意,我可以扩展我的答案添加一个例子。
    • 是的,非常感谢您提供打包和发送结构的示例,谢谢。
    • uint32_t 数据 = (uint32_t)(*bytes);这是错误的:P
    • 将随机位置投射到uint32_t 是不安全的,您应该始终使用memcpy,例如参见stackoverflow.com/questions/46790550/…
    【解决方案3】:

    使用 IEEE-754(网络字节顺序或大端序)传输二进制浮点的协议示例是 IPFIX。你可以看看它是怎么做的。

    无论如何,如果你想要一些可移植的东西,你应该选择一些独立于架构的编码。我建议你以 JSON 为例。

    【讨论】:

      猜你喜欢
      • 2013-01-06
      • 1970-01-01
      • 2019-01-21
      • 2019-05-17
      • 1970-01-01
      • 1970-01-01
      • 2018-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多