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

Documentos relacionados