Networking
Transcrição
Networking
Networking Networking auf dem iPhone Christoph Wulf Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 1 / 34 Abstraktionsebenen Next Step Networking Objective-C einfach Core Foundation Networking C exibel BSD Sockets C nur für Nerds Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 2 / 34 Abstraktionsebenen Next Step Networking Objective-C einfach Core Foundation Networking C exibel BSD Sockets C nur für Nerds Komponenten für TCP und UDP Sockets HTTP, HTTPS, FTP und andere gängige Protokolle Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 2 / 34 Abstraktionsebenen Next Step Networking Objective-C einfach Core Foundation Networking C exibel BSD Sockets C nur für Nerds Komponenten für TCP und UDP Sockets HTTP, HTTPS, FTP und andere gängige Protokolle Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 3 / 34 Asynchrone Kommunikation und Nebenläugkeit Interaktionen mit dem Netzwerk (insbesondere Internet) kosten vergleichsweise viel Zeit Nebenläugkeit für Reaktivität erforderlich mehrere Threads Run Loops für non-blocking I/O Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 4 / 34 Run Loops Schleife, die Ereignisse abarbeitet Button gedrückt Timer abgelaufen Kompassrichtung geändert Ereignisschleife schon beim Speichermanagement relevant (Freigabe durch autorelease) App hat implizit schon einen Run Loop durch das UI Betriebssystem kümmert sich darum, Ereignisse in die Schleife einzureihen Vorteil: nur ein Thread, dennoch Reaktivität Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 5 / 34 Streams und Sockets Stream: unidirektionale Verbindung Eingabestrom NSInputStream (z.B. zum Lesen aus einer Datei) Ausgabestrom NSOutputStream (z.B. zum Schreiben in eine Datei) Socket: bidirektionale Verbindung Kombination aus Ein- und Ausgabestrom beide Ströme sind mit demselben Remote-Socket verbunden Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 6 / 34 Stream Delegate Delegates für Ein- und Ausgabestrom werden über Ereignisse benachrichtigt. Zentrale Ereignisse Neue Daten sind verfügbar (Eingabestrom) und können mit read:maxLength gelesen werden. Ausgabestrom ist bereit, weitere Daten zu senden und kann mit write:maxlength beschrieben werden. Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 7 / 34 Ereignisse Ströme kennen nur eine Delegate-Methode: (void) stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent NSStreamEventNone NSStreamEventOpenCompleted NSStreamEventHasBytesAvailable NSStreamEventHasSpaceAvailable NSStreamEventErrorOccurred NSStreamEventEndEncountered Networking (iPhone-Praktikum) Es ist absolut nichts passiert. Strom wurde erfolgreich erönet. Neue Daten können gelesen werden. Neue Daten können geschrieben werden. Ein Fehler ist aufgetreten. EOF bzw. Remote Stream geschlossen Christoph Wulf 10.05.2010 8 / 34 Framework einbinden #include <CFNetwork/CFNetwork.h> #include <sys/socket.h> #include <netinet/in.h> Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 9 / 34 Socket-Verbindung herstellen CFStringRef host = (CFStringRef) @"remotehost.net"; CFReadStreamRef in; CFWriteStreamRef out; CFStreamCreatePairWithSocketToHost(NULL, host, 1001, &in, &out); inputStream = [(NSInputStream*) in retain]; outputStream = [(NSOutputStream*) out retain]; [inputStream setDelegate:self]; [outputStream setDelegate:self]; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; [outputStream open]; Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 10 / 34 Ereignisse behandeln (Lesen) - (void)stream:(NSStream*) stream handleEvent:(NSStreamEvent) streamEvent { switch(streamEvent) { case NSStreamEventHasBytesAvailable: uint8_t buffer[1024]; unsigned int read = [(NSInputStream*) stream read:buffer maxLength:1024]; [data appendBytes:(const void*) buffer length:read]; // Überprüfen, ob eine Nachricht vorliegt, ggf. behandeln break; // ... andere Ereignisse } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 11 / 34 Ereignisse behandeln (Schreiben) - (void)stream:(NSStream*) stream handleEvent:(NSStreamEvent) streamEvent { switch(streamEvent) { case NSStreamEventHasSpaceAvailable: // Pointer auf die Bytes des Puffers uint8_t *bytesToWrite = (uint8_t *)[data mutableBytes]; // Pointer an Stelle des Puffers verschieben bytesToWrite += index; int maxLength = [data maxLength] - index; unsigned int length = (maxLength >= 1024) ? 1024 : maxLength; uint8_t buffer[length]; (void)memcpy(buffer, bytesToWrite, length); length = [stream write:(const uint8_t *)buffer maxLength:length]; index += length; break; // ... andere Ereignisse } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 12 / 34 Problem Lesen von Natur aus asynchron Schreiben eher ein synchroner Vorgang Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 13 / 34 Problem Lesen von Natur aus asynchron Schreiben eher ein synchroner Vorgang Sofern man nicht dauerhaft in den Stream schreibt, woher weiÿ der Stream, wann er NSStreamEventHasSpaceAvailable signalisieren soll? Immer, auch wenn nichts geschrieben wird ⇒ Busy Waiting Solange auch etwas geschrieben wird, danach nicht mehr ⇒ Wann darf man dann später wieder schreiben? Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 13 / 34 Lösung Stream sendet kontinuierlich NSStreamEventHasSpaceAvailable, solange auch etwas hineingeschrieben wird. Wird gerade nichts in den Stream geschrieben, ersten Teilbereich von Daten über write hineinschreiben, danach über das Delegate lässt sich über [stream hasBytesAvailable] prüfen (keine Race-Condition möglich!) ansonsten einfach in den Puer des Delegates einreihen Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 14 / 34 Lösung Stream sendet kontinuierlich NSStreamEventHasSpaceAvailable, solange auch etwas hineingeschrieben wird. Wird gerade nichts in den Stream geschrieben, ersten Teilbereich von Daten über write hineinschreiben, danach über das Delegate lässt sich über [stream hasBytesAvailable] prüfen (keine Race-Condition möglich!) ansonsten einfach in den Puer des Delegates einreihen Möglichkeit für eine eigene Abstraktion, die das vereinfacht Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 14 / 34 Ereignisse behandeln (Verbindungsabbruch) - (void)stream:(NSStream*) stream handleEvent:(NSStreamEvent) streamEvent { switch(streamEvent) { case NSStreamEventEndEncountered: [stream close]; [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [stream release]; stream = nil; // ggf. Benutzer informieren, falls Abbruch nicht erwartet break; // ... andere Ereignisse } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 15 / 34 Ereignisse behandeln (Fehlerfall) - (void)stream:(NSStream*) stream handleEvent:(NSStreamEvent) streamEvent { switch(streamEvent) { case NSStreamEventErrorOccurred: NSError* error = [stream streamError]; // Fehlermeldung break; // ... andere Ereignisse } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 16 / 34 URL Loading Anfragen an URLs über NSURLRequest oder NSMutableURLRequest Verbindung über NSURLConnection Asynchrone Ergebnisverarbeitung über Delegate zu NSURLConnection Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 17 / 34 URL Loading Anfragen an URLs über NSURLRequest oder NSMutableURLRequest Verbindung über NSURLConnection Asynchrone Ergebnisverarbeitung über Delegate zu NSURLConnection NSURL *url = [NSURL URLWithString:@"http://www.server.de/pageOrFile.xyz"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease]; NSAssert(connection != nil, @"Failed to create URL connection."); responseData = [[NSMutableData data] retain]; Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 17 / 34 Delegate Methoden connection:willSendRequest:redirectResponse connection:canAuthenticateAgainstProtectionSpace connectionShouldUseCredentialStorage connection:didReceiveAuthenticationChallenge connection:didCancelAuthenticationChallenge connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite connection:didReceiveResponse connection:didReceiveData connection:willCacheResponse connection:didFailWithError connectionDidFinishLoading Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 18 / 34 Delegate Methoden (Minimum) (void) connection:(NSURLConnection*) connection didReceiveResponse:(NSURLResponse*) response wird aufgerufen, bevor didReceiveData aufgerufen wird kann mehrfach aufgerufen werden (z.B. bei Redirect oder Temporarily Moved) Aufgaben Puer für zu empfangene Daten initialisieren Erwartete Länge und Mime-Type aus NSURLResponse auslesen Status Code und andere Header aus NSHTTPURLResponse auslesen (im Falle von HTTP/HTTPS) Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 19 / 34 Delegate Methoden (Minimum) (void) connection:(NSURLConnection*) connection didReceiveData:(NSData*) data wird für empfangene Teildaten aufgerufen wird daher in der Regel mehrfach aufgerufen Aufgaben Daten verarbeiten (wenn möglich) Daten an den Puer anhängen Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 20 / 34 Delegate Methoden (Minimum) (void) connection:(NSURLConnection*) connection didFailWithError:(NSError*) error wird im Fehlerfall aufgerufen letzter Aufruf der URLConnection an ihr Delegate Aufgaben Fehlermeldung Freigabe der URLConnection Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 21 / 34 Delegate Methoden (Minimum) (void) connectionDidFinishLoading:(NSURLConnection *)connection wird nach der Übertragung aufgerufen letzter Aufruf der URLConnection an ihr Delegate Aufgaben Verarbeitung der Daten im Puer Freigabe der URLConnection Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 22 / 34 Implementierung: didReceiveResponse - (void) connection:(NSURLConnection*) connection didReceiveResponse:(NSURLResponse*) response { [responseData setLength:0]; length = [response expectedContentLength]; if (length > 0.0) { [progressIndicator setIndeterminate:NO]; [progressIndicator setDoubleValue:0]; [progressIndicator setMaxValue:length]; } else { [progressIndicator setIndeterminate:YES]; [progressIndicator startAnimation]; } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 23 / 34 Implementierung: didReceiveData - (void) connection:(NSURLConnection*) connection didReceiveData:(NSData*) data { [responseData appendData:data]; if ([progressIndicator isIndeterminate] == NO) { [progressIndicator incrementBy:(double) [data length]]; } } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 24 / 34 Implementierung: didFailWithError - (void) connection:(NSURLConnection*) connection didFailWithError:(NSError*) error { [connection release]; [responseData release]; [progressIndicator stopAnimation]; // Fehlermeldung } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 25 / 34 Implementierung: connectionDidFinishLoading - (void) connectionDidFinishLoading:(NSURLConnection*) connection { [connection release]; [progressIndicator stopAnimation]; // Weiterverarbeitung der Daten } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 26 / 34 Implementierung: connectionDidFinishLoading - (void) connectionDidFinishLoading:(NSURLConnection*) connection { [connection release]; [progressIndicator stopAnimation]; NSString *responseString = [[NSString alloc] initWithData:responseData encoding: NSUTF8StringEncoding]; [responseData release]; // Weiterverarbeitung der Zeichenkette } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 27 / 34 Implementierung: connectionDidFinishLoading - (void) connectionDidFinishLoading:(NSURLConnection*) connection { [connection release]; [progressIndicator stopAnimation]; NSXMLParser *parser = [[NSXMLParser alloc] initWithData:responseData]; [parser setDelegate:self]; [parser parse]; [parser release]; [responseData release]; } Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 28 / 34 Cache [NSURLRequest requestWithURL:url] [NSURLRequest requestWithURL:url cachePolicy:<<policy>> timeoutInterval: 10.0] NSURLRequestUseProtocolCachePolicy was HTTP bzw. HTTPS vorschreibt NSURLRequestReloadIgnoringCacheData immer vom Server laden NSURLRequestReturnCacheDataElseLoad wenn möglich aus Cache beziehen, sonst laden NSURLRequestReturnCacheDataDontLoad nur aus dem Cache beziehen, sonst streiken Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 29 / 34 HTTP Post NSURL *url = [NSURL URLWithString:@"http://www.server.de/some.cgi"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:body]; // Body vom Typ NSData Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 30 / 34 HTTP Post (HTML-Formular mit Datei) Format: Content-Type: multipart/form-data; boundary=0xKhTmLbOuNdArY Content-Disposition: form-data; name="feld1" wert1 --0xKhTmLbOuNdArY Content-Disposition: form-data; name="feld2" wert2 --0xKhTmLbOuNdArY Content-Disposition: form-data; name="upload"; filename="gewinn.pdf" Content-Type: application/octet-stream << Datei binär >> --0xKhTmLbOuNdArY-- Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 31 / 34 HTTP Post (HTML-Formular mit Datei) NSURL *url = [NSURL URLWithString:@"http://www.someserver.de/some.cgi"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPMethod:@"POST"]; NSString *boundary = @"0xKhTmLbOuNdArY"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; [request addValue:contentType forHTTPHeaderField: @"Content-Type"]; NSMutableData *body = [NSMutableData data]; NSData *utf8boundary = [[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]; Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 32 / 34 HTTP Post (HTML-Formular mit Datei) // Ein Formularfeld [body appendData:utf8boundary]; NSString conDisp = @"Content-Disposition: form-data; name=\"feld1\"\r\n\r\n"; [body appendData:[conDisp dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"wert1" dataUsingEncoding:NSUTF8StringEncoding]]; // Eine Datei [body appendData:utf8boundary]; conDisp = @"Content-Disposition: form-data; name=\"upload\"; filename=\" gewinn.pdf\"\r\n"; [body appendData:[conDisp dataUsingEncoding:NSUTF8StringEncoding]]; NSString contentType = @"Content-Type: application/octet-stream\r\n\r\n"; [body appendData:[contentType dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[NSData dataWithContentsOfFile:@"/myVirus.exe"]]; [body appendData:utf8boundary]; [body appendData:[@"--\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [request setHTTPBody:body]; Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 33 / 34 HTTP Post (HTML-Formular mit Datei) Wer häug HTTP Post senden will, sollte sich eine abstrahierende Klasse schreiben oder die Library evaluieren! MYNetwork Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 34 / 34 HTTP Post (HTML-Formular mit Datei) Wer häug HTTP Post senden will, sollte sich eine abstrahierende Klasse schreiben oder die Library evaluieren! MYNetwork Networking (iPhone-Praktikum) Christoph Wulf 10.05.2010 34 / 34