Reçu beaucoup d'informations gracieuseté de Quinn "The Eskimo" chez Apple. Hélas, vous avez ici la mauvaise extrémité de la crosse. URLSessionStreamTask permet de résoudre une connexion TCP (ou TLS sur TCP) sans le cadre HTTP. Vous pouvez le considérer comme un équivalent de haut niveau à l'API BSD Sockets.
Le codage de transfert en bloc fait partie de HTTP et est donc pris en charge par tous les autres types de tâche URLSession (tâche de données, tâche de téléchargement, tâche de téléchargement). Vous n'avez pas besoin de faire quelque chose de spécial pour activer cela. Le codage de transfert en bloc est une partie obligatoire de la norme HTTP 1.1 et est donc toujours activé.
Vous avez cependant une option quant à la façon dont vous recevez les données renvoyées. Si vous utilisez les API de commodité URLSession (dataTask(with:completionHandler:)
et ainsi de suite), URLSession mettra en mémoire tampon toutes les données entrantes et les transmettra ensuite à votre gestionnaire de complétion dans une grande valeur Data
. C'est pratique dans de nombreuses situations, mais cela ne fonctionne pas bien avec une ressource en streaming. Dans ce cas, vous devez utiliser les API basées sur les délégués URLSession (dataTask(with:)
et ainsi de suite), qui appellera la méthode déléguée de session urlSession(_:dataTask:didReceive:)
avec des blocs de données à leur arrivée. En ce qui concerne l'extrémité spécifique que je testais, les éléments suivants ont été découverts: Il semble que le serveur active uniquement sa réponse de diffusion en continu (l'encodage de transfert en bloc) si le client lui envoie une requête de diffusion en continu. C'est un peu bizarre, et certainement pas requis par la spécification HTTP.
Heureusement, il est possible de forcer URLSession d'envoyer une demande de diffusion en continu:
Créer votre tâche avec uploadTask(withStreamedRequest:)
Mettre en œuvre la méthode de délégué urlSession(_:task:needNewBodyStream:)
pour retourner un flux d'entrée qui, à la lecture, renvoie le corps de la demande
Profit!
J'ai joint un code de test qui montre cela en action.Dans ce cas, il utilise une paire de flux liés, en passant le flux d'entrée à la requête (par l'étape 2 ci-dessus) et en conservant le flux de sortie.
Si vous souhaitez réellement envoyer des données dans le corps de la demande, vous pouvez le faire en écrivant dans le flux de sortie.
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")
}
}
}
Et vous pouvez appeler le code de réseau de partout tels que:
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)
}
}
Hope this helps.
Avez-vous déjà résolu cela? J'étais à la recherche d'heures moi-même. La documentation est vraiment clairsemée. – Ryan
@Ryan vient de poster une réponse maintenant! –