2017-05-07 60 views
1

我的目标是通过以网络字节顺序以64位无符号整数开头的网络发送数据报。所以首先我用宏的数量转变为大端:将64位无符号整数转换为字符缓冲区C

#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) 
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) 

然后,我把它序列化到一个缓冲区:

unsigned char * serialize_uint64(unsigned char *buffer, uint64_t value) { 
    printf("**** seriializing PRIu64 value = %"PRIu64"\n", value); 
    int i; 
    for (i = 0; i < 8; i++) 
    buffer[i] = (value >> (56 - 8 * i)) & 0xFF; 
    for (i = 0; i < 8; i++) 
    printf("bufer[%d] = %x\n", i, buffer[i]); 

    return buffer + 8; 
} 

然后我反序列化与

uint64_t deserialize_uint64(unsigned char *buffer) { 
    uint64_t res = 0; 
    printf("*** deserializing buffer:\n"); 
    int i; 
    for (i = 0; i < 8; i++) 
    printf("bufer[%d] = %x\n", i, buffer[i]); 
    for (i = 0; i < 8; i++) 
    res |= buffer[i] << (56 - 8 * i); 


    return res; 
} 

似乎为小整数工作但以下测试代码无法正常工作:

uint64_t a = (uint64_t) time(NULL); 
printf("PRIu64: a =%"PRIu64"\n", a); 

uint64_t z = htonll(a); 
uint64_t zz = ntohll(z); 
printf("z = %"PRIu64" ==> zz = %"PRIu64" \n", z, zz); 

unsigned char buffer[1024]; 
serialize_uint64(buffer, z); 
uint64_t b = deserialize_uint64(buffer); 
uint64_t c = ntohll(g); 

我得到

a = 1494157850 
htonll(a) = 1876329069679738880 ==> ntohll(htonll(a)) = 1494157850 
**** seriializing PRIu64 value = 1876329069679738880 
bufer[0] = 1a 
bufer[1] = a 
bufer[2] = f 
bufer[3] = 59 
bufer[4] = 0 
bufer[5] = 0 
bufer[6] = 0 
bufer[7] = 0 
********* 
*** deserializing buffer: 
bufer[0] = 1a 
bufer[1] = a 
bufer[2] = f 
bufer[3] = 59 
bufer[4] = 0 
bufer[5] = 0 
bufer[6] = 0 
bufer[7] = 0 
===> res = 436866905 
c = 6417359100811673600 

好像缓冲区没有捕捉较大的那个数...

+0

编辑:它适用于32位所以'2,147,483,647',但它崩溃'2,147,483,648' – micsza

回答

1

你的串行基本上是

unsigned char *serialize_u64(unsigned char *buffer, uint64_t value) 
{ 
    buffer[7] = value & 0xFF; 
    value >>= 8; 
    buffer[6] = value & 0xFF; 
    value >>= 8; 
    buffer[5] = value & 0xFF; 
    value >>= 8; 
    buffer[4] = value & 0xFF; 
    value >>= 8; 
    buffer[3] = value & 0xFF; 
    value >>= 8; 
    buffer[2] = value & 0xFF; 
    value >>= 8; 
    buffer[1] = value & 0xFF; 
    value >>= 8; 
    buffer[0] = value & 0xFF; 
    return buffer + 8; 
} 

,并从本地字节顺序串行value网络字节顺序;不需要宏。

所以,它看起来像OP的serialize_uint64()应该工作得很好。只是没有字节顺序的宏应该被使用。

在移位之前,OP的deserialize_uint64()应该投下buffer[i](uint64_t),以确保移位的结果是64位。就个人而言,我更喜欢写解串器作为

unsigned char *serialize_u64(unsigned char *buffer, uint64_t *valueptr) 
{ 
    uint64_t value = buffer[0]; 
    value <<= 8; 
    value |= buffer[1]; 
    value <<= 8; 
    value |= buffer[2]; 
    value <<= 8; 
    value |= buffer[3]; 
    value <<= 8; 
    value |= buffer[4]; 
    value <<= 8; 
    value |= buffer[5]; 
    value <<= 8; 
    value |= buffer[6]; 
    value <<= 8; 
    value |= buffer[7]; 
    *valueptr = value; 
    return buffer + 8; 
} 

这不等同操作OP的,如果OP使用res |= ((uint64_t)buffer[i]) << (56 - 8 * i);代替。

同样,串行器和解串器已经将数据转换为/从网络字节顺序转换为本地字节顺序;根本不应该使用字节顺序宏。

+0

铸造到'uint64_t'已经帮助 - 非常感谢!但是,在字节顺序宏 - 如果我想这是可移植的,我不知道运行机器的字节序是什么,我需要以某种方式区分 - 是正确的还是我错过了什么? – micsza

+0

@ micsza:是的,你错过了一些东西。正如我所解释的,所显示的代码将“值”从网络字节顺序转换为本地字节顺序。这与本地字节顺序无关。代码本身非常便携。 –

+0

@micsza:至于为什么不需要字节顺序转换,我们需要看看涉及的数学。在网络字节顺序中,值的第一个字节是最重要的一个;对于64位值,它包含位56到63(含)。数学函数实现时,使用位移和掩码直接从64位值中提取此字节值和其他字节值。本地字节顺序无关紧要,因为在64位值上完成的数学运算完全不依赖于本地字节顺序。 –

相关问题