NSURLSession versteht Antwort von Webservice (JSON Stream) nicht. Alternative Herangehensweisen?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • NSURLSession versteht Antwort von Webservice (JSON Stream) nicht. Alternative Herangehensweisen?

    HTTP Noob trifft auf komischen Webservice. ;)

    ich bastel an einer iPhone App welche mit der Smart Home Server Software auf meinem Raspberry Pi reden soll.
    Das klappt für normales Pollen der Daten über NSURLSession super, der Server antwortet brav mit JSON Objekten.

    Nun würde ich aber gern in verschiedenen Ansichten Live-Updates des Gerätestatus haben anstatt nur alle XX Sekunden zu pollen.

    Websockets kann die Software nicht, nur Long Polling.

    Allerdings scheint sich die Software beim Antworten nicht so ganz an normale Webstandards zu halten.
    Die NSURLSession rennt irgendwann in den TimeOut, liefert mir aber data=nil zurück, da sie wohl immer noch auf das Ende der Antwort wartet.

    Ich hab von kmr den Tip bekommen meine iPhone Verbindung durch das Debugger-Proxy Charles umzuleiten (auf dem Mac installiert).
    Dort sehe ich, dass aber wirklich Daten fließen.
    Immer nur ein paar Zeilen JSON für jedes Smart-Home Event, die Verbindung bleibt danach weiter offen.
    Beim nächsten Event kommen dann wieder ein paar Zeilen.

    Die NSURLSession rennt irgendwann in den TimeOut, liefert mir aber data=nil zurück, da sie wohl immer noch auf das Ende der Antwort wartet.

    Wie kann man das alternativ angehen um die Daten häppchenweise zu empfangen direkt wenn sie ankommen (so wie ich das auch in Charles sehe)?

    Zum Ablauf:
    - Ich rufe eine URL mit dem Schema auf:

    Quellcode

    1. 192.168.1.203:9000/smarthome?inform=type=status;fmt=JSON
    Das inform=type=status ist die Schnittstelle für das Abholen von auftretenden Events über Long Poll.

    Schicke ich das über NSURLSession ab:

    Quellcode

    1. let request = NSMutableURLRequest(URL: url)
    2. let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
    3. print(data) // nil nach Timeout
    4. print(error) // Error wegen TimeOut
    5. }
    6. task.resume()
    sehe ich in Charles folgende Infos:
    Status Receiving response body…
    Response Code 200 OK
    Protocol HTTP/1.1
    SSL -
    Method GET
    Kept Alive Yes
    Content-Type application/octet-stream; charset=UTF-8

    und es kommt als response vom Smart-Home Server:

    Quellcode

    1. HTTP/1.1 200 OK
    2. Content-Type: application/octet-stream; charset=UTF-8
    Dann ist Stille bis irgendwas im Smart-Home passiert.
    In diesem Falle hab ich ein Fensterkontakt geöffnet.

    In Charles ergänzt sich die Response dann um:

    Quellcode

    1. ["WindowContact01","auf","<div id=\"WindowContact01\" … Zeile gekürzt … </div>"]
    2. ["WindowContact01-state","open","open"]
    3. ["WindowContact01-state-ts","2016-01-14 20:39:30","2016-01-14 20:39:30"]

    Das ist nun genau das was ich gern in der iOS App empfangen würde,
    leider lässt sich NSURLSession nicht dazu bewegen mir diese Daten weiterzureichen. ;)

    Hab mir heute schon einen Wolf gegoogled.
    Aber da das meine erste App ist bei der ich mit einem Webservice rede, bin ich schon überfragt wonach ich eigentlich so recht suchen muss...

    Hilfe wird dankbar angenommen... :love:
  • Wahrscheinlich handelt es sich um eine WebSockets-schnittstelle und nicht eine einfache http-schnittstelle oder?

    Falls doch HTTP, dann wirst du das timeout manuell erhöhen müssen.
    Auch kannst du nicht auf das ende der verbindung warten weil es dieses ja nicht gibt. du musst also die delegates einbauen die dich informieren wenn teildaten reingekommen sind (und diese dann verarbeiten).
  • gandhi schrieb:

    macmoonshine schrieb:

    Ich verstehe das Problem noch nicht so ganz.
    Es handelt sich um eine Verbindung, die die ganze Zeit offenbleibt. Immer wenn was passiert, fliesst wieder ein Stückchen JSON-Text. Das mag' wohl NSURLSession nicht.
    Dann würde ich auch eine stehende Verbindung über NSStream implementieren. In dem Beispielprojekt zu meinem Macoun-Vortrag müssten dazu schon eine ganze Reihe Bausteine für's HTTP-Parsing sein. Den Server kann man wahrscheinlich auch ganz gut zu einem Client umdrehen. ;)

    EDIT: Vielleicht geht es auch über das Data-Delegate. Diese API ist zumindest für den stückweisen Datenempfang ausgelegt.
    „Meine Komplikation hatte eine Komplikation.“

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von macmoonshine ()

  • macmoonshine schrieb:

    gandhi schrieb:

    macmoonshine schrieb:

    Ich verstehe das Problem noch nicht so ganz.
    Es handelt sich um eine Verbindung, die die ganze Zeit offenbleibt. Immer wenn was passiert, fliesst wieder ein Stückchen JSON-Text. Das mag' wohl NSURLSession nicht.
    Dann würde ich auch eine stehende Verbindung über NSStream implementieren. In dem Beispielprojekt zu meinem Macoun-Vortrag müssten dazu schon eine ganze Reihe Bausteine für's HTTP-Parsing sein. Den Server kann man wahrscheinlich auch ganz gut zu einem Client umdrehen. ;)
    naja, dann muss man sich halt um wirklich alles selbst kümmern. in dem fall auch um SSL...

    falls die-server-software offen ist (geh ich mal davon aus wenn sie auf dem raspi läuft), dann würd ich der eventuell websockets beibringen (also am projekt mitarbeiten).
  • Ich würde es auch zuerst mit dem Data-Task probieren (s. oben). ;) :D

    gritsch schrieb:

    naja, dann muss man sich halt um wirklich alles selbst kümmern. in dem fall auch um SSL...
    Bei einem Home-Server auf einem Raspberry-PI kann (oder muss) man vielleicht auf SSL verzichten. ;)

    Für das Parsing und die Erzeugung der HTTP-Header gibt's was Fertiges von Apple.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Ich würde es auch zuerst mit dem Data-Task probieren (s. oben). ;) :D

    gritsch schrieb:

    naja, dann muss man sich halt um wirklich alles selbst kümmern. in dem fall auch um SSL...
    Bei einem Home-Server auf einem Raspberry-PI kann (oder muss) man vielleicht auf SSL verzichten. ;)
    Für das Parsing und die Erzeugung der HTTP-Header gibt's was Fertiges von Apple.
    habe oben gesehn dass es eben SSL ist - und wenns schon da ist, warum dann drauf verzichten?
    klar, das parsen ist kein problem.
  • Mach es mit delegates wie von gritsch vorgeschlagen.
    Nimmt keine shardSession sonder erzeug deine eigene und setz den Timeout in der NSURLSessionConfiguraton auf Anschlag.

    macmoonshine schrieb:




    Für das Parsing und die Erzeugung der HTTP-Header gibt's was Fertiges von Apple.
    Wo?

    Chris
    Man macht einfach solange irgendwelche Dinge, bis man tot ist.
    Und dann bekommen die anderen Kuchen.
  • Ach verdammt, die weiteren Delegates wie NSURLSessionTaskDelegate, NSURLSessionDataDelegate und NSURLSessionDownloadDelegate hab ich gar nicht gesehen.
    Klarer Fall von RTFM. ;)

    Vielen Dank für die zahlreichen Posts und Hinweise. :)
    Dann werd ich das mal testen und hoffe ich komme um NSStream herum.


    edit: @gandhi ist FHEM (fhem.de/fhem.html)
    Das SmartHome Projekt mit der wohl altbackendsten Oberfläche (sieht genauso aus wie die Homepage :D)
    Aber hab bisher nichts gefunden was sich extremer verbiegen und konfigurieren lässt.
    Gibt auch massig Module für jeden Blödsinn.
    Z.B. Einbindung des Hubs meiner Harmony Universal-Ferbedienung. So weiß das SmartHome z.B. wann Fernsehen eingeschaltet wird und kann entsprechend andere Dinge anpassen (z.B. Licht abhängig von Helligkeit oder Uhrzeit usw.)
    Oder Einbinden eines HomeKit Servers, womit man dann auch über Siri das SmartHome steuern kann, obwohl eigentlich keine HomeKit kompatible Hardware verbaut ist.

    Nur Webinterface und verfügbare Apps sind nicht auf der Höhe von anderen SmartHome Lösungen, daher will ich mir was angepasstes für mich basteln...
    Da ich bisher nur Apps mit lokaler Datenhaltung erstellt habe, ist das ganze auch eine nette Übung um mal ein wenig im Netzwerk rumzuspuken.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Thyraz ()

  • Thyraz schrieb:

    edit: @gandhi ist FHEM (fhem.de/fhem.html)
    OK, habe ich mir auch überlegt, mich aber dann für Homematic entschieden: Die Zentrale läuft out-of-the-box ohne Basteln und installieren und ist auch nicht wesentlich teurer als ein Raspi + Gehäuse + Netzteile + Funkstick.

    Nachdem es für Homematic zwar ein Haufen Apps für jeden Geschmack gibt, aber nix für den Mac bin ich am Überlegen da was zu machen. Mal gucken.

    Apropos Harmony: Da habe ich auch eine und die Konfigurationssoftware ("Harmony Remote Software") ist ja wohl das schlechteste Stück Software was mir die letzten 15 Jahre untergekommen ist.
  • Ja, die Zentralen der Hersteller sind natürlich weit weniger Aufwand vom Einrichten. ;)
    FHEM kannst ja auch mit HomeMatic Aktoren/Sensoren betreiben. Ich hab bisher aber das meiste Zeug aus dem Z-Wave System.

    Harmony ist echt eine Grütze einzurichten. Aber ne gescheite Alternative die besser wäre scheint es ja nicht wirklich zu geben. ;)
    Immerhin muss man an der Config ja nicht andauernd herumschrauben...
  • Vielen Dank nochmal an alle. :)

    Kleiner Stolperstein war noch, dass ich dann nicht die Block based Syntax nehmen darf.
    Steht auch in der Doku, dass sonst die Delegate Calls nicht aufgerufen werden.

    Bekomme nun bei jedem Event den entsprechenden Text zurückgeliefert den ich nach DeviceNamen durchsuchen kann um diese dann upzudaten falls sie gerade angezeigt werden.
  • Thyraz schrieb:

    Vielen Dank nochmal an alle. :)

    Kleiner Stolperstein war noch, dass ich dann nicht die Block based Syntax nehmen darf.
    Steht auch in der Doku, dass sonst die Delegate Calls nicht aufgerufen werden.

    Bekomme nun bei jedem Event den entsprechenden Text zurückgeliefert den ich nach DeviceNamen durchsuchen kann um diese dann upzudaten falls sie gerade angezeigt werden.
    klar, aber achtung, du musst bei den blöcken die du geliefert bekommst warten bis du das fertige JSON bekommen hast (denn du kannst das in teilen bekommen).
    Da du die länge ja nicht kennst, musst du wohl nach gleich viel schließenden unescaped klammern suchen wie es offene gibt... (oder gibts sonst einen unique separator?
  • Muss mir den Eventstream mal genauer anschauen, aber an sich sieht das aus, als ob es keine verschachtelten Arrays gibt.
    Somit müsste ich mir ja nur anschauen, ob nach der letzten geschlossenen eckigen Klammer noch was kommt.

    Wenn ja dies vorne an die nächste Nachricht anhängen.

    Damit sollte ich umgehen können, dass ich einen Devicenamen nicht mitbekomme falls er über 2 Data Objekte verteilt reinkommt.

    Die eigentlichen JSON Nachrichten sind mir an sich egal.
    Da gibt es zu viel verschiedene Statusmeldungen je nach Gerätetyp und Event.

    Werde daher nur nach den Namen parsen und die Geräte dann mit einem entsprechenden Befehl refreshen.