我使用自定义的 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”时尽可能地对其进行膨胀,这解决了我的问题,但我不确定我的解决方案是否存在任何风险。 如果有人能帮助我,我将不胜感激。
有没有其他方法来处理我的问题,或者我的解决方案是否足够安全,可以投入使用。
有什么理由不能只使用 WebSockets 和 NSURLSessionWebSocketTask 吗? 看起来您正在重新发明轮子,并且使用标准协议和内置 API 将比滚动您自己的自定义解决方案容易很多,更不用说可能更快、更可靠。
无论如何,简短的答案是,gzip 压缩数据的结构从压缩表开始,随着编码器接收更多数据,压缩表会随着时间而变化。 通过使用与数据对象绑定的压缩器(每次都会改变),它试图使用标准基线解码表,并且在第一块或第二块之后将不起作用(如果有的话)。
解码类似内容的唯一方法是使用解码器的单个实例,该解码器实例被分配并存储在挂起实际 NSURLSessionTask 对象的对象中 - 要么与关联的对象一起(假设您没有使用后台会话),要么通过使用任务标识符和会话对象的组合作为 NSDictionary 中的键。 这样,您就可以将后续块提供给“相同”解码器实例,并且它可以使用正确的符号表对其进行解码。 您可以从这个库开始:
https://www.zlib.net/或者,正如我所说,只需使用操作系统提供的功能来执行此操作(如果需要使用该标准协议,则更改服务器端)。