1

嗨,根据这个答案,我写了子类,NSInputStream它工作得很好。

现在事实证明,我有一个场景,我正在向服务器提供大量数据并防止其他服务饥饿,我需要控制提供数据的速度。因此,我通过以下条件改进了子类的功能:

  • 当数据应该被推迟时,hasBytesAvailable返回NO和读取尝试以读取的零字节结束
  • 当可以发送数据时,- read:maxLength:允许一次读取一些最大数量的数据(默认为 2048)。
  • - read:maxLength:返回读取的零字节时,计算所需的延迟并在延迟NSStreamEventHasBytesAvailable事件发布之后。

这是代码的有趣部分(它与 C++ 混合):

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
    if (![self isOpen]) {
        return kOperationFailedReturnCode;
    }
    int delay =  0;
    NSInteger readCount = (NSInteger)self.cppInputStream->Read(buffer, len, delay);
    if (readCount<0) {
        return kOperationFailedReturnCode;
    }
    LOGD("Stream") << __PRETTY_FUNCTION__
            << " len: " << len
            << " readCount: "<< readCount
            << " time: " << (int)(-[openDate timeIntervalSinceNow]*1000)
            << " delay: " << delay;

    if (!self.cppInputStream->IsEOF()) {
        if (delay==0)
        {
            [self enqueueEvent: NSStreamEventHasBytesAvailable];
        } else {
            NSTimer *timer = [NSTimer timerWithTimeInterval: delay*0.001
                                                     target: self
                                                   selector: @selector(notifyBytesAvailable:)
                                                   userInfo: nil
                                                    repeats: NO];

            [self enumerateRunLoopsUsingBlock:^(CFRunLoopRef runLoop) {
                CFRunLoopAddTimer(runLoop, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
            }];
        }
    } else {
        [self setStatus: NSStreamStatusAtEnd];
        [self enqueueEvent: NSStreamEventEndEncountered];
    }

    return readCount;
}

- (void)notifyBytesAvailable: (NSTimer *)timer {
    LOGD("Stream") << __PRETTY_FUNCTION__ << "notifyBytesAvailable time: " << (int)(-[openDate timeIntervalSinceNow]*1000);

    [self enqueueEvent: NSStreamEventHasBytesAvailable];
}

- (BOOL)hasBytesAvailable {
    bool result = self.cppInputStream->HasBytesAvaible();
    LOGD("Stream") << __PRETTY_FUNCTION__ << ": " << result << " time: " << (int)(-[openDate timeIntervalSinceNow]*1000);
    return result;
}

我为此写了一些测试,它奏效了。

当我将此流NSURLSession用作 HTTP 请求的正文源时出现问题。从日志中我可以看到它NSURLSession试图一次读取所有内容。在第一次阅读时,我返回有限部分的数据。之后立即NSURLSession询问是否有可用的字节(我返回NO)。一段时间后(例如 170 毫秒),我发送通知说字节现在可用但NSURLSession不响应并且不调用我的流类的任何方法。

这是我在日志中看到的(运行一些测试时):

09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper open]
09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper hasBytesAvailable]: 1 time: 0
09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper read:maxLength:] len: 32768 readCount: 2048 time: 0 delay: 170
09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper hasBytesAvailable]: 0 time: 0
09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper hasBytesAvailable]: 0 time: 0
09:32:14990[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper hasBytesAvailable]: 0 time: 0
09:32:15161[0x7000002a0000] D/Stream: -[CSCoreFoundationCppInputStreamWrapper notifyBytesAvailable:]notifyBytesAvailable time: 171

其中时间是自流打开以来的毫秒数。

LooksNSURLSession无法处理数据速率有限的输入流。还有其他人有类似的问题吗?或者有替代概念如何实现带宽管理NSURLSession

4

2 回答 2

1

我可以支持的解决方案是:

  1. 使用来自 iOS9 和 OSX10.11 的 NSURLSessionStreamTask。
  2. 改为使用 ASIHTTPRequest。
于 2016-11-12T14:54:27.893 回答
0

不幸的是,NSInputStream是一个类集群。这使得子类化变得困难。在 的情况下NSInputStream,任何子类都完全不受支持,并且可能会以令人着迷的方式失败。(有关详细信息,请参阅http://blog.bjhomer.com/2011/04/subclassing-nsinputstream.html。)

而不是子类化NSInputStream,您应该使用一对绑定的流并创建自己的数据提供者类来向其中提供数据。去做这个:

  • 打电话CFStreamCreateBoundPair
  • 将结果CFReadStream对象转换为NSInputStream指针。
  • CFWriteStream对象转换为NSOutputStream指针。
  • 在创建上传任务或请求对象时传递输入流。
  • 创建一个使用计时器定期将下一个数据块传递到输出流的类。

如果您这样做,您的数据提供者类传递给的数据将可用于从另一端NSOutputStream读取。NSInputStream

于 2016-11-12T22:12:17.130 回答