如何使用 URLSessionStreamTask 和 URLSession 进行分块编码传输

编程入门 行业动态 更新时间:2024-10-25 18:28:12
本文介绍了如何使用 URLSessionStreamTask 和 URLSession 进行分块编码传输的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在尝试连接到 Twitter 流 API 端点.看起来 URLSession 支持通过 URLSessionStreamTask 进行流式传输,但是我不知道如何使用 API.我也找不到任何示例代码.

I am trying to connect to the Twitter streaming API endpoint. It looks like URLSession supports streaming via URLSessionStreamTask, however I can't figure out how to use the API. I have not been able to find any sample code either.

我尝试测试以下内容,但没有记录网络流量:

I tried testing the following, but there is no network traffic recorded:

let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let stream = session.streamTask(withHostName: "stream.twitter/1.1/statuses/sample.json", port: 22) stream.startSecureConnection() stream.readData(ofMinLength: 0, maxLength: 100000, timeout: 60, completionHandler: { (data, bool, error) in print("bool = (bool)") print("error = (String(describing: error))") }) stream.resume()

我还实现了委托方法(包括 URLSessionStreamDelegate),但它们没有被调用.

I've also implemented the delegate methods (including URLSessionStreamDelegate), but they do not get called.

如果有人代码发布了如何为来自流式端点的 chunked 响应打开持久连接的示例,那将非常有帮助.另外,我正在寻找不涉及第三方库的解决方案.与 stackoverflow/a/9473787/5897233 类似但使用等效的 URLSession 更新的响应是理想的.

It would be really helpful if someone code post a sample of how to open a persistent connection for chunked responses from a streaming endpoint. Also, I am seeking solutions which don't involve third party libraries. A response similar to stackoverflow/a/9473787/5897233 but updated with the URLSession equivalent would be ideal.

注意:上面的示例代码中省略了授权信息.

推荐答案

收到了 Apple 的 Quinn The Eskimo"提供的大量信息.

Received lots of info courtesy of Quinn "The Eskimo" at Apple.

唉,你在这里搞错了.URLSessionStreamTask 用于处理裸 TCP(或 TLS over TCP)连接,顶部没有 HTTP 框架.您可以将其视为高级别的 BSD 套接字 API.

Alas, you have the wrong end of the stick here. URLSessionStreamTask is for wrangling a naked TCP (or TLS over TCP) connection, without the HTTP framing on top. You can think of it as a high-level equivalent to the BSD Sockets API.

分块传输编码是 HTTP 的一部分,因此所有其他 URLSession 任务类型(数据任务、上传任务、下载任务)都支持.你不需要做任何特别的事情来启用它.分块传输编码是 HTTP 1.1 标准的强制性部分,因此始终处于启用状态.

The chunked transfer encoding is part of HTTP, and is thus supported by all of the other URLSession task types (data task, upload task, download task). You don’t need to do anything special to enable this. The chunked transfer encoding is a mandatory part of the HTTP 1.1 standard, and is thus is always enabled.

但是,您可以选择如何接收返回的数据.如果您使用 URLSession 便利 API(dataTask(with:completionHandler:) 等),URLSession 将缓冲所有传入的数据,然后将其传递给一个大型 Data值.这在许多情况下很方便,但不适用于流式资源.在这种情况下,您需要使用基于 URLSession 委托的 API(dataTask(with:) 等),它将调用 urlSession(_:dataTask:didReceive:) 带有数据块的会话委托方法.

You do, however, have an option as to how you receive the returned data. If you use the URLSession convenience APIs (dataTask(with:completionHandler:) and so on), URLSession will buffer all the incoming data and then pass it to your completion handler in one large Data value. That’s convenient in many situations but it doesn’t work well with a streamed resource. In that case you need to use the URLSession delegate-based APIs (dataTask(with:) and so on), which will call the urlSession(_:dataTask:didReceive:) session delegate method with chunks of data as they arrive.

至于我正在测试的特定端点,发现了以下内容:如果客户端向其发送流式请求,服务器似乎仅启用其流式响应(分块传输编码).这有点奇怪,而且 HTTP 规范绝对不需要.

As for the specific endpoint I was testing, the following was uncovered: It seems that the server only enables its streaming response (the chunked transfer encoding) if the client sends it a streaming request. That’s kinda weird, and definitely not required by the HTTP spec.

幸运的是,可以强制 URLSession 发送流式请求:

Fortunately, it is possible to force URLSession to send a streaming request:

  • 使用 uploadTask(withStreamedRequest:)

    实现 urlSession(_:task:needNewBodyStream:) 委托方法以返回输入流,读取时返回请求正文

    Implement the urlSession(_:task:needNewBodyStream:) delegate method to return an input stream that, when read, returns the request body

    利润!

    我附上了一些测试代码来展示这一点.在这种情况下,它使用一对绑定的流,将输入流传递给请求(按照上面的步骤 2)并保持输出流.

    I’ve attached some test code that shows this in action. In this case it uses a bound pair of streams, passing the input stream to the request (per step 2 above) and holding on to the output stream.

    如果您想实际发送数据作为请求正文的一部分,您可以通过写入输出流来实现.

    If you want to actually send data as part of the request body you can do so by writing to the output stream.

    class NetworkManager : NSObject, URLSessionDataDelegate { static var shared = NetworkManager() private var session: URLSession! = nil override init() { super.init() let config = URLSessionConfiguration.default config.requestCachePolicy = .reloadIgnoringLocalCacheData self.session = URLSession(configuration: config, delegate: self, delegateQueue: .main) } private var streamingTask: URLSessionDataTask? = nil var isStreaming: Bool { return self.streamingTask != nil } func startStreaming() { precondition( !self.isStreaming ) let url = URL(string: "ENTER STREAMING URL HERE")! let request = URLRequest(url: url) let task = self.session.uploadTask(withStreamedRequest: request) self.streamingTask = task task.resume() } func stopStreaming() { guard let task = self.streamingTask else { return } self.streamingTask = nil task.cancel() self.closeStream() } var outputStream: OutputStream? = nil private func closeStream() { if let stream = self.outputStream { stream.close() self.outputStream = nil } } func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) { self.closeStream() var inStream: InputStream? = nil var outStream: OutputStream? = nil Stream.getBoundStreams(withBufferSize: 4096, inputStream: &inStream, outputStream: &outStream) self.outputStream = outStream completionHandler(inStream) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { NSLog("task data: %@", data as NSData) } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let error = error as NSError? { NSLog("task error: %@ / %d", error.domain, error.code) } else { NSLog("task complete") } } }

    您可以从任何地方调用网络代码,例如:

    And you can call the networking code from anywhere such as:

    class MainViewController : UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if NetworkManager.shared.isStreaming { NetworkManager.shared.stopStreaming() } else { NetworkManager.shared.startStreaming() } self.tableView.deselectRow(at: indexPath, animated: true) } }

    希望这会有所帮助.

  • 更多推荐

    如何使用 URLSessionStreamTask 和 URLSession 进行分块编码传输

    本文发布于:2023-11-14 04:49:47,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1586266.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:如何使用   URLSessionStreamTask   URLSession

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!