看到@ MartinR的回答後,我重新閱讀了Apple Docs,並在NSRunLoop
活動上做了一些閱讀。解決方案並不像我第一次想到的那樣微不足道,而且需要一些額外的緩衝。
結論
雖然雷Wenderlich例子有效,那它就不是最佳 - 由@MartinR指出,如果在傳出TCP窗口無房,調用write:maxLength
將阻止。 Ray Wenderlich的例子確實起作用的原因是因爲發送的消息很少且不經常發生,並且給出了無差錯和大帶寬的互聯網連接,它「很可能」會工作。當你開始處理(很多)更多數據(更多)的數據時,這些write:maxLength:
調用可能會開始阻止,並且應用程序將開始失速。
對於NSStreamEventHasSpaceAvailable
事件,蘋果的文檔有以下建議:如果委託收到一個NSStreamEventHasSpaceAvailable事件並沒有寫任何東西到流
,它不接收從運行進一步的空間可用事件直到NSOutputStream對象接收更多的字節。 ......您可以讓委託在接收到NSStreamEventHasSpaceAvailable事件時不寫入流時設置一個標誌。後來,當你的程序有更多的字節要寫入時,它可以檢查這個標誌,如果設置了,直接寫入輸出流實例。
因此只有「保證安全」調用write:maxLength:
在兩種情況下:
- 回調(在收到
NSStreamEventHasSpaceAvailable
事件)內。
- 只有當我們已經收到
NSStreamEventHasSpaceAvailable
,但在回調本身內部選擇不呼叫write:maxLength:
(例如我們沒有實際寫入的數據)時,回調之外。
對於方案(2),我們將不會再收到回調,直到write:maxLength
實際上是直接調用 - 蘋果建議設置委託回調內的標誌(見上文)來表示,當我們被允許這樣做。
我的解決方案是使用額外的緩衝水平 - 添加一個NSMutableArray
作爲數據隊列。我寫數據到套接字代碼看起來像這樣(的意見和錯誤檢查不再贅述,在currentDataOffset
變量指示我們有多麼的「當前」 NSData
對象的已發送):
// 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;
}
}
}
感謝您的洞察力,蜉蝣,看起來像我和你一樣有困惑。再次感謝! – Patricia
請注意,bytesWritten可能是負數,表示出現錯誤,因此如果出現錯誤,上述代碼將會卡住,因爲currentDataOffset wouldnt匹配...編輯建議 –
您能否幫助我瞭解接收器如何知道它是否只接收另一個大塊的數據或數據作爲一個整體?也許我的緩衝區容量是2字節,我一次只能寫兩個ASCII字符。接收者如何告訴我是否發送「do」或「dope」字樣? –