Nachdem ich @ MartinRs Antwort gesehen habe, lese ich die Apple Docs erneut und lese etwas über NSRunLoop
Ereignisse. Die Lösung war nicht so trivial wie ich zuerst dachte und erfordert eine zusätzliche Pufferung.
Schlussfolgerungen
Während der Ray Wenderlich Beispiel funktioniert, ist es nicht optimal ist - wie von @MartinR erwähnt, wenn es kein Raum in dem ausgehenden TCP-Fenster ist, wird der Anruf an write:maxLength
blockieren. Der Grund, warum das Beispiel von Ray Wenderlich funktioniert, ist, dass die gesendeten Nachrichten klein und selten sind und bei einer fehlerfreien Internetverbindung mit großer Bandbreite "wahrscheinlich" funktionieren wird. Wenn Sie beginnen, mit (viel) größeren Datenmengen zu arbeiten, die (viel) häufiger gesendet werden, können die write:maxLength:
Anrufe jedoch blockieren und die App wird zum Stillstand kommen ...
Für den NSStreamEventHasSpaceAvailable
Fall Apples Dokumentation folgende Ratschläge hat:
Wenn die Delegierten ein NSStreamEventHasSpaceAvailable Ereignis empfangen und alles, was in den Stream nicht schreiben, es erhält keine weiteren raum verfügbaren Ereignisse aus dem Laufe Schleife, bis das NSOutputStream-Objekt mehr Bytes empfängt. ... ... Sie können festlegen, dass der Delegat ein Flag setzt, wenn er beim Empfang eines NS- StreamEventHasSpaceAvailable-Ereignisses nicht in den Stream schreibt. Später, wenn Ihr Programm mehr Bytes zum Schreiben hat, kann es dieses Flag überprüfen und, falls gesetzt, direkt in die Output-Stream-Instanz schreiben.
Es ist daher nur ‚garantiert sicher zu sein‘ write:maxLength:
in zwei Szenarien zu nennen:
- Innerhalb des Rückrufs (bei Empfang des
NSStreamEventHasSpaceAvailable
Ereignisses).
- Außerhalb des Rückrufs, wenn und nur wenn wir bereits die
NSStreamEventHasSpaceAvailable
empfangen haben, aber nicht innerhalb des Callbacks write:maxLength:
aufrufen (z. B. hatten wir keine Daten zu schreiben).
Für Szenario (2), werden wir den Rückruf wieder nicht empfangen, bis write:maxLength
tatsächlich direkt aufgerufen wird - von Apple schlägt eine Flagge innerhalb der Delegierten Rückruf-Einstellung (siehe oben), um anzuzeigen, wenn wir dies tun dürfen.
Meine Lösung war eine zusätzliche Pufferungsebene - Hinzufügen einer NSMutableArray
als Datenwarteschlange. Mein Code für Daten an einen Socket schreiben sieht wie folgt aus (Kommentare und Fehler der Kürze halber weggelassen Prüfung, die currentDataOffset
Variable gibt an, wie viel von der ‚aktuellen‘ NSData
Objekt, das wir gesendet haben):
// Public interface for sending data.
- (void)sendData:(NSData *)data {
[_dataWriteQueue insertObject:data atIndex:0];
if (flag_canSendDirectly) [self _sendData];
}
// NSStreamDelegate message
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
// ...
case NSStreamEventHasSpaceAvailable: {
[self _sendData];
break;
}
}
// Private
- (void)_sendData {
flag_canSendDirectly = NO;
NSData *data = [_dataWriteQueue lastObject];
if (data == nil) {
flag_canSendDirectly = YES;
return;
}
uint8_t *readBytes = (uint8_t *)[data bytes];
readBytes += currentDataOffset;
NSUInteger dataLength = [data length];
NSUInteger lengthOfDataToWrite = (dataLength - currentDataOffset >= 1024) ? 1024 : (dataLength - currentDataOffset);
NSInteger bytesWritten = [_outputStream write:readBytes maxLength:lengthOfDataToWrite];
currentDataOffset += bytesWritten;
if (bytesWritten > 0) {
self.currentDataOffset += bytesWritten;
if (self.currentDataOffset == dataLength) {
[self.dataWriteQueue removeLastObject];
self.currentDataOffset = 0;
}
}
}
Danke für die Einsicht, Eintagsfliegen , sieht aus, als hätte ich das gleiche Maß an Verwirrung wie du. Danke noch einmal! – Patricia
Beachten Sie, dass bytesWritten negativ sein kann, was auf einen Fehler hinweist, daher würde der obige Code im Falle eines Fehlers hängen bleiben, weil currentDataOffset nicht übereinstimmen würde ... Vorschlag bearbeiten –
Könnten Sie mir helfen zu verstehen, wie der Empfänger weiß, ob er nur einen anderen empfängt Stück Daten oder Daten als Ganzes? Vielleicht hat mein Puffer eine Kapazität von 2 Bytes und ich kann nur zwei ASCII-Zeichen gleichzeitig schreiben. Wie der Empfänger sagt, ob ich "do" oder "dope" Worte an ihn sende? –