如何在 XCode 中从 NSInputStream 中可靠地检索 NSData 对象

How to reliably retrieve NSData objects from NSInputStream in XCode

所以我的应用程序是按照这些思路工作的:

  1. iPod 不断发送 NSDictionaries,其中包含:以 JPEG 编码的图像和一些图像属性作为 NSStrings。
  2. NSDictionary 使用 NSPropertyListSerialization 编码,格式为 BinaryFormat_v1_0,并通过 NSStream 以 1024 字节的数据包形式发送到中央计算机 运行 OSX 上的一个应用程序。
  3. OSX 应用程序接收数据包,不断附加到单个 NSMutableData 对象,直到它看到下一个 NSData 对象的第一个数据包(我发现它以二进制格式开始为 'bplist').
  4. 通过调用 NSPropertyListSerialization,NSData 被转换回 NSDictionary 供 OSX 应用程序使用。
  5. 一旦 NSData 成功转换(或未成功),NSData 对象将设置回零以开始读取下一轮数据包。

更多注意事项:NSInputStream 和 NSOutput 流都在各自设备的 NSDefaultRunLoopMode 中的 currentRunLoop 上 运行。

当 运行 这个过程时,有时转换回 NSDictionary 工作正常,没有错误(大约 1/3 的尝试),但其他时候转换 returns 这个错误:

Error: Failed to convert NSData to NSDict : Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected character b at line 1" UserInfo={NSDebugDescription=Unexpected character b at line 1, kCFPropertyListOldStyleParsingError=Error Domain=NSCocoaErrorDomain Code=3840 "Conversion of string failed." UserInfo={NSDebugDescription=Conversion of string failed.}}

以下是从流中解析数据的程序部分:

...处理流事件的方法:

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
    case NSStreamEventHasBytesAvailable: {

        uint8_t buf[1024];
        unsigned int len = (unsigned)[(NSInputStream *)aStream read:buf maxLength:1024];

        if(len) {
            [self handleEventBuffer:buf WithLength:len];
        }
...

...以及处理数据的方法:

-(void)handleEventBuffer:(uint8_t*)buf WithLength:(unsigned int)len {
...
NSString *bufStr = [NSString stringWithFormat:@"%s",(const char*)buf];
        if ([bufStr containsString:@"bplist00"] && [self.cameraData length] > 0) {
            // Detected new file, enter in all the old data and reset for new data
            NSError *error;
            NSDictionary *tempDict = [[NSDictionary alloc] init];

            tempDict = [NSPropertyListSerialization propertyListWithData:self.cameraData
                                                             options:0
                                                              format:NULL
                                                               error:&error];

            if (error != nil) {
                // Expected good file but no good file, erase and restart
                NSLog(@"Error: Failed to convert NSData to NSDict : %@", [error description]);
                [self.cameraData setLength:0];
            } 
...
            [self.cameraData setLength:0];
            [self.cameraData appendBytes:buf length:len];

        } else {
            // Still recieving data
            [self.cameraData appendBytes:buf length:len];
        }

所以,我要问的问题是:

您似乎依赖于对流的每次写入都会产生相同大小的匹配读取,您知道 NSStream 可以保证这一点吗?如果不是,那么任何读取都可能包含两个(或更多)编码词典的一部分,并且您会得到您看到的解析错误。

替代方法:

对于每个要发送的编码词典:

写入结束:

  1. 发送一条消息,其中包含随后的编码字典的字节大小。
  2. 分块写编码字典,最后一个块可能很短
  3. 重复

阅读结束:

  1. 读取指定其确切字节长度的大小消息。
  2. 分块读取编码字典,确保只读取 (1) 报告的字节数。
  3. 重复。

如果您使用的是可靠的通信流,这应该使您能够可靠地阅读每个编码字典。它避免了您试图弄清楚每个编码字典之间的边界在哪里,因为该信息是您协议的一部分。

HTH