2017-05-07 80 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位值上完成的數學運算完全不依賴於本地字節順序。 –

相關問題