iOS自定义NSURLProtocol响应数据编码问题

问题描述 投票:0回答:1

我使用自定义的 NSURLProtocol 类来实现 IP 直连。现在从我们的服务器云接收到的数据格式是gzip,所以我会在编码格式为“gzip”时对数据进行膨胀,下面是关键代码:

//encoding format
...
NSString *contentEncoding = headerFields[@"Content-Encoding"];
self.isGzipType = contentEncoding.length > 0 && [contentEncoding isEqualToString:@"gzip"];
...

// callback
- (void)handleStreamDataDidReceive:(NSInputStream *)aInputStream statusCode:(long)statusCode {
    uint8_t buffer[16 * 1024];
    uint8_t *buf = NULL;
    NSUInteger length = 0;
    if (![aInputStream getBuffer:&buf length:&length]) {
        NSInteger amount = [self.inputStream read:buffer maxLength:sizeof(buffer)];
        buf = buffer;
        length = amount;
    }
    
    // Assuming the data is complete and ready to be processed
    if (length > 0) {
        NSData *data = [[NSData alloc] initWithBytes:buf length:length];
        if (self.isGzipType) {
            NSData *gzipData = [data gzipInflate];
            if (gzipData != nil && gzipData.length > 0) {
                data = gzipData;
            }
        }
        [self.client URLProtocol:self didLoadData:data];
}

当数据量不大时,即 case 'NSStreamEventHasBytesAvailable' 的委托方法只调用一次,就可以了。

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    if (eventCode == NSStreamEventHasBytesAvailable) {
        if (![aStream isKindOfClass:[NSInputStream class]]) {
            return;
        }
        [self onStreamHasBytesAvailable:aStream];
    }
    ...

现在,如果案例“NSStreamEventHasBytesAvailable”的委托方法被调用多次, 我将从

NSData *gzipData = [data gzipInflate];
获得零数据,因此“self.client”会收到错误响应。 我认为这是因为“gzip”数据片段无法膨胀。你们以前遇到过这个问题吗?你们是如何解决的?

我用以下方法处理这个问题:

- (void)handleStreamDataDidReceive:(NSInputStream *)aInputStream statusCode:(long)statusCode {
    uint8_t buffer[16 * 1024];
    uint8_t *buf = NULL;
    NSUInteger length = 0;
    if (![aInputStream getBuffer:&buf length:&length]) {
        NSInteger amount = [self.inputStream read:buffer maxLength:sizeof(buffer)];
        buf = buffer;
        length = amount;
    }
    
    // Assuming the data is complete and ready to be processed
    if (length > 0) {
        
        NSData *data = [[NSData alloc] initWithBytes:buf length:length];
        if (!self.receivedData) {
            self.receivedData = [NSMutableData data];
        }
        
        [self.receivedData appendData:data];
     }
}

else if (eventCode == NSStreamEventEndEncountered) {
        
        if (self.isGzipType) {
            self.receivedData = [self.receivedData gzipInflate];
        }
        [self.client URLProtocol:self didLoadData:self.receivedData];
        
        [self closeStream:aStream];
        [self.client URLProtocolDidFinishLoading:self];
    }

如您所见,我将每个片段数据附加在我自己的“receivedData”中,并在“eventCode == NSStreamEventEndEncountered”时尽可能地对其进行膨胀,这解决了我的问题,但我不确定我的解决方案是否存在任何风险。 如果有人能帮助我,我将不胜感激。

有没有其他方法来处理我的问题,或者我的解决方案是否足够安全,可以投入使用。

ios nsurlprotocol
1个回答
0
投票

有什么理由不能只使用 WebSockets 和 NSURLSessionWebSocketTask 吗? 看起来您正在重新发明轮子,并且使用标准协议和内置 API 将比滚动您自己的自定义解决方案容易很多,更不用说可能更快、更可靠。

无论如何,简短的答案是,gzip 压缩数据的结构从压缩表开始,随着编码器接收更多数据,压缩表会随着时间而变化。 通过使用与数据对象绑定的压缩器(每次都会改变),它试图使用标准基线解码表,并且在第一块或第二块之后将不起作用(如果有的话)。

解码类似内容的唯一方法是使用解码器的单个实例,该解码器实例被分配并存储在挂起实际 NSURLSessionTask 对象的对象中 - 要么与关联的对象一起(假设您没有使用后台会话),要么通过使用任务标识符和会话对象的组合作为 NSDictionary 中的键。 这样,您就可以将后续块提供给“相同”解码器实例,并且它可以使用正确的符号表对其进行解码。 您可以从这个库开始:

https://www.zlib.net/

或者,正如我所说,只需使用操作系统提供的功能来执行此操作(如果需要使用该标准协议,则更改服务器端)。

© www.soinside.com 2019 - 2024. All rights reserved.