代码之家  ›  专栏  ›  技术社区  ›  Hayden Evans

NSData数据包解释

  •  0
  • Hayden Evans  · 技术社区  · 12 年前

    关于我正在制作的应用程序中数据包的解释,我有一个相当复杂的问题。主机应用程序向具有以下结构的客户端应用程序发送数据包:

    [头10个字节][所选可变字节长度客户端的peerID][空字节][可变字节长度的客户端的peer ID][空白字节][4个字节的整数][可变比特长度客户端的peerID][空闲字节][四个字节的复数]

    以下是在这种结构下生成的示例数据包:

    434e4c50 00000000006a3134 31303837 34393634 00313233 38313638 35383900 000003e8 31343130 38373439 36340000 0003e8

    转换后看起来是这样的:

    CNLP电话:1410874964 1238168589 141087496 4

    “CNLP j”是10个字节的数据包标头。“1410874964”是所选客户端的peerID。“1238168589”是另一个客户端的peerID。“”的int值为1000。“1410874964”是另一个客户端(在本例中为所选客户端)的peerID。“”的int值也为1000。基本上,在这个数据包中,我正在传达两件事——选择的客户端是谁以及与每个客户端相关的int值。

    我的问题存在于口译方面(客户方面)。为了解释这种特定类型的数据包,我使用以下方法:

        + (NSMutableDictionary *)infoFromData:(NSData *)data atOffset:(size_t) offset
    {
        size_t count;
    
        NSMutableDictionary *info = [NSMutableDictionary dictionaryWithCapacity:8];
    
        while (offset < [data length])
        {
            NSString *peerID = [data cnl_stringAtOffset:offset bytesRead:&count];
            offset += count;
    
            NSNumber *number = [NSNumber numberWithInteger:[data cnl_int32AtOffset:offset]];
            offset += 4;
    
            [info setObject:number forKey:peerID];
        }
    
        return info;
    }
    

    通常,这些数据包中的每个数据包的范围在49到51个字节之间。“偏移量”在前面的方法中设置,以反映数据包标头后的字节数加上所选播放器后的空字节(在上述数据包的情况下,为21)。“count”被初始化为值1。在该特定示例的情况下,长度为51。以下方法传递了上述参数:

        - (NSString *)cnl_stringAtOffset:(size_t)offset bytesRead:(size_t *)amount
    {
        const char *charBytes = (const char *)[self bytes];
        NSString *string = [NSString stringWithUTF8String:charBytes + offset];
        *amount = strlen(charBytes + offset) + 1;
        return string;
    }
    

    该方法应该读取数据包中的可变长度字符串,将偏移量设置为peerID字符串后面的空字节填充后的字节,并返回读取的字符串。然后将“amount”设置为该方法为字符串读取的字节数(返回到第一个方法后,这将成为count的新值)。然后,“offset”和“count”被加在一起,成为新的“偏移量”——数据包的int部分的解释将从这里开始。将上述参数传递给以下方法:

    - (int)cnl_int32AtOffset:(size_t)offset
    {
        const int *intBytes = (const int *)[self bytes];
        return ntohl(intBytes[offset / 4]);
    }
    

    此方法旨在返回在数据包的当前偏移值处读取的32位(4字节)int值。我认为,当偏移量是一个不能被4整除的数字时,这种方法就存在问题。在这种情况下,第一个int值1000被正确解释,32在while循环的第一次迭代期间被返回作为偏移量。然而,在第二次迭代过程中,解释的int值为909377536(从读取数据包中的字节36340000而不是字节000003E8中获得)。这可能是由于该迭代过程中的偏移量被设置为47(不可被4整除)。在解释了上述类别中的32位int之后,在第一种方法中,将4添加到偏移量,以说明4字节(32位int)。如果我关于偏移量不可被零整除的直觉是正确的,那么任何解决这个问题的建议都将不胜感激。很长一段时间以来,我一直在寻找解决这个问题的方法,也许新的眼光会有所帮助。谢谢你的帮助!!!

    1 回复  |  直到 12 年前
        1
  •  0
  •   tc.    12 年前

    不可移植的版本(由于多种原因,未定义的行为):

    return ntohl(*(const int *)([self bytes]+offset));
    

    半可移植版本有些棘手,但是 in C99 看来你可以假设 int32_t 是“通常的”二的补码表示(没有陷阱表示,没有填充位),因此:

    // The cast is necessary to prevent arithmetic on void* which is nonstandard.
    const uint8_t * p = (const uint8_t *)[self bytes]+offset;
    // The casts ensure the result type is big enough to hold the shifted value.
    // We use uint32_t to prevent UB when shifting into the sign bit.
    uint32_t n = ((uint32_t)p[0]<<24) | ((uint32_t)p[1]<<16) | ((uint32_t)p[2]<<8) | ((uint32_t)p[3]);
    // Jump through some hoops to prevent UB on "negative" numbers.
    // An equivalent to the third expression is -(int32_t)~n-1.
    // A good compiler should be able to optimize this into nothing.
    return (n <= INT32_MAX) ? (int32_t)n : -(int32_t)(UINT32_MAX-n)-1;
    

    这在没有8位字节的体系结构上是不起作用的,但这种体系结构可能对如何在网络上传输内容有不同的约定。

    一个好的编译器应该能够在合适的体系结构上将其优化为单个(可能是字节交换的)加载。