Получил много информации от Куинна «Эскимо» из Apple.
Увы, здесь у вас не тот конец палки. URLSessionStreamTask предназначен для обработки открытого соединения TCP (или TLS через TCP) без HTTP-фрейминга сверху. Вы можете думать об этом как о высокоуровневом эквиваленте BSD Sockets API.
Кодирование групповой передачи является частью HTTP и, таким образом, поддерживается всеми другими типами задач URLSession (задача данных, задача загрузки, задача загрузки). Вам не нужно делать ничего особенного, чтобы включить это. Кодирование передачи по частям является обязательной частью стандарта HTTP 1.1 и поэтому всегда включено.
Однако у вас есть возможность выбора способа получения возвращаемых данных. Если вы используете удобные API-интерфейсы URLSession (dataTask(with:completionHandler:)
и т. д.), URLSession буферизует все входящие данные, а затем передает их обработчику завершения в одном большом значении Data
. Это удобно во многих ситуациях, но плохо работает с потоковым ресурсом. В этом случае вам нужно использовать API-интерфейсы на основе делегата URLSession (dataTask(with:)
и т. д.), которые будут вызывать метод делегата сеанса urlSession(_:dataTask:didReceive:)
с фрагментами данных по мере их поступления.
Что касается конкретной конечной точки, которую я тестировал, было обнаружено следующее: кажется, что сервер разрешает свой потоковый ответ (фрагментированное кодирование передачи), только если клиент отправляет ему потоковый запрос. Это довольно странно и определенно не требуется спецификацией HTTP.
К счастью, можно заставить URLSession отправлять потоковый запрос:
Создайте свою задачу с помощью uploadTask(withStreamedRequest:)
Реализуйте метод делегата urlSession(_:task:needNewBodyStream:)
для возврата входного потока, который при чтении возвращает тело запроса.
Выгода!
Я прикрепил тестовый код, который показывает это в действии. В этом случае он использует связанную пару потоков, передавая входной поток запросу (согласно шагу 2 выше) и удерживая выходной поток.
Если вы действительно хотите отправить данные как часть тела запроса, вы можете сделать это, написав в выходной поток.
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")
}
}
}
И вы можете вызывать сетевой код из любого места, например:
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)
}
}
Надеюсь это поможет.
person
iOS dev
schedule
06.09.2017