iOS:如何通过HTTP流式传输(实时发送)数据?

时间:2017-07-05 09:15:26

标签: ios http streaming

我知道,这个主题的标题非常通用,我认为这个问题很容易解决(我很容易在Android上制作)。但是在iOS上......嗯,这个问题存在......

我有一台设备,我想在其上传输数据(音频数据准确)。我必须使用的协议是HTTP。实际上,我必须构建ans发送一个包含其身体中的一些数据的HTTP请求,然后,当我的“生产者”(这里是重新采样/编码音频数据的过程)给出要传输的新数据时,通过它发送它请求机构。这对于HTTP来说听起来很奇怪,但它就像没有边界的HTTP多部分(数据只是一个接一个地发送)。

例如,在Java中,我使用URL和标题初始化HttpUrlConnection,我将Content-Length标题精确到“9999999”(因为它是在目标设备的文档中写的){{1} }。我没有调用/*my connection*/.setFixedLengthStreamingMode(9999999);方法,但我得到输出流(connect()),当我的制作人提供新数据时,我只需拨打getOutputStream()然后/*my output stream*/.write(/*my data*/);

所以,我尝试使用/*my output stream*/.flush();和低级API NSURLSessionNSInputStream。但在每种情况下,我都无法做到:

  • 使用NSOutputStream我调用NSURLSession但回调uploadTaskWithStreamedRequest:仅调用一次(或者在新身份验证时调用两次)。所以我只在输入流中发送一个数据包。

  • 在我发送任何标准TCP以输入流结尾的数据(HTTP标头除外)之前,目标设备关闭needNewBodyStream:NSInputStream连接没有错误。

    < / LI>

我认为这是许多人所做的事情,他们是我没有看到的方式,不是吗?请帮助我给我举例(这将是完美的)或关于我必须选择什么API或要应用的特定参数?

谢谢!

修改

我的问题类似于 this ,但这是解释它的另一种(更短)方式。

编辑2

这是我使用NSURLSession时的代码(如果可能,我更喜欢继续使用):

NSOutputStream

确切地说,我没有在网址中输入身份验证信息。代表的方法按此顺序调用:NSURLSessionConfiguration* defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSOperationQueue* operationQueue = [NSOperationQueue mainQueue]; NSURLSession* session = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:self delegateQueue:operationQueue]; NSInputStream* inputStream = [NSInputStream inputStreamWithData:/*data*/]; NSURL* url = /*init from string*/ NSMutableRequest* request : [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; request.HTTPBodyStream = inputStream; /*Add some headers to request*/ NSURLSessionUploadTask* uploadTask = [session uploadTaskWithStreamedRequest:request]; [uploadTask resume]; - &gt; didSendBodyData(这是我执行身份验证的地方,它起作用,因为我在401之前收到,之后没有收到) - &gt; didReceiveChallenge(这是我将数据放入完成处理程序的输入流中的位置) - &gt; needNewBodyStream - &gt; didSendBodyData - &gt; didFinishCollectingMetrics(代码为-1005:kCFErrorDomainCFNetwork)。奇怪的是,数据传输得很好(第一个数据包),因为我可以在某些瞬间听到目标设备中的声音。

更准确地说,这是连接期间的网络流量:

  • 发送 TCP SYN,ECN,CWR
  • Rcv TCP SYN,ACK,ECN
  • 发送 TCP 确认
  • 发送重新组装的PDU的 TCP 段(我的HTTP请求和数据,不带 auth)(x7,每个后跟ACK响应)
  • Rcv HTTP 401(因为需要摘要)
  • ... TCP ACK,FIN / ACK,SYN / ECN / CWR ......
  • 的继承
  • 发送重组PDU的 TCP 段(我的HTTP请求和带 auth的数据)(x7,每个后跟ACK响应)

然后(我不知道为什么):

  • Rcv TCP END,ACK
  • 发送 TCP 确认
  • 发送 TCP END,确认
  • Rcv TCP 确认

1 个答案:

答案 0 :(得分:0)

好的,我已经得到了答案,我认为分享它是一个好主意。实际上这很容易:诀窍根本就不明确。回调needNewBodyStream:被调用一次(或更多,请参阅doc),我们只需要将NSInputStream对象与NSOutputStream对象绑定。然后,当“生产者”提供新数据时,数据将在NSOutputStream的回调中写入。以前,我的连接已关闭,因为在needNewBodyStream通话后没有任何内容可以发送。

NSURLSessionTaskDelegate实施的一部分:

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
    needNewBodyStream:(void (^)(NSInputStream * bodyStream))completionHandler
{
    NSInputStream* inputStream = nil;
    NSOutputStream* outputStream = nil;

    // Bind inputStream and outputStream

    self.inputStream = inputStream;
    self.outputStream = outputStream;
    [self.outputStream setDelegate:/*NSStreamDelegate implementation*/];
    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [self.outputStream open];

    completionHandler(self.inputStream);
}

NSStreamDelegate实施的一部分:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            // to handle...
            break;
        case NSStreamEventHasSpaceAvailable:
            // Producer/Consumer's consumer here, and when data is available:
            [self.outputStream write:/*data*/ maxLength:/*data length*/];                
            break;
        case NSStreamEventErrorOccurred:
            // to handle...
            break;
        case NSStreamEventEndEncountered:
            // to handle...
            break;
        default:
            break;
    }
}

现在您可以通过HTTP流式传输“in live”,享受:)