2013-04-28 39 views
8

NSOutputStreamがデータの送信を終了したら、どのように接続を閉じることができますか?NSOutputStreamが終了したときに接続を閉じる

私が探し回ったところでは、サーバが接続を切断すると、イベントNSStreamEventEndEncounteredが呼び出されることがわかりました。 OutputStreamが送信するデータを終了していない場合

StreamStatusは常に0(接続クローズ)または2(接続オープン)ですが、決して4(データ書き込み)です。

上記の2つの方法は、書き込みプロセスについて私に十分な情報を伝えていないため、ストリームがまだ書き込み中かどうかを判断できないため、または終了して今接続を閉じることができます。

5日間のgooglelingと試してみると、私は完全にアイデアがありません。要求どおりのおかげ

EDIT追加されたコード:

- (void)startSend:(NSString *)filePath 

{ 

BOOL     success; 

NSURL *     url; 



assert(filePath != nil); 

assert([[NSFileManager defaultManager] fileExistsAtPath:filePath]); 

assert([filePath.pathExtension isEqual:@"png"] || [filePath.pathExtension isEqual:@"jpg"]); 



assert(self.networkStream == nil);  // don't tap send twice in a row! 

assert(self.fileStream == nil);   // ditto 



// First get and check the URL. 

... 
.... 
..... 


// If the URL is bogus, let the user know. Otherwise kick off the connection. 

... 
.... 
..... 


if (! success) { 

    self.statusLabel.text = @"Invalid URL"; 

} else { 



    // Open a stream for the file we're going to send. We do not open this stream; 

    // NSURLConnection will do it for us. 



    self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath]; 

    assert(self.fileStream != nil); 



    [self.fileStream open]; 



    // Open a CFFTPStream for the URL. 



    self.networkStream = CFBridgingRelease(

     CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url) 

    ); 

    assert(self.networkStream != nil); 



    if ([self.usernameText.text length] != 0) { 

     success = [self.networkStream setProperty:self.usernameText.text forKey:(id)kCFStreamPropertyFTPUserName]; 

     assert(success); 

     success = [self.networkStream setProperty:self.passwordText.text forKey:(id)kCFStreamPropertyFTPPassword]; 

     assert(success); 

    } 



    self.networkStream.delegate = self; 

    [self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    ///////******** LINE ADDED BY ME TO DISONNECT FROM FTP AFTER CLOSING CONNECTION *********//////////// 

    [self.networkStream setProperty:(id)kCFBooleanFalse forKey:(id)kCFStreamPropertyFTPAttemptPersistentConnection]; 

    ///////******** END LINE ADDED BY ME *********//////////// 

    [self.networkStream open]; 



    // Tell the UI we're sending. 



    [self sendDidStart]; 

} 

} 



- (void)stopSendWithStatus:(NSString *)statusString 

{ 

if (self.networkStream != nil) { 

    [self.networkStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    self.networkStream.delegate = nil; 

    [self.networkStream close]; 

    self.networkStream = nil; 

} 

if (self.fileStream != nil) { 

    [self.fileStream close]; 

    self.fileStream = nil; 

} 

[self sendDidStopWithStatus:statusString]; 

} 



- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 

// An NSStream delegate callback that's called when events happen on our 

// network stream. 

{ 

#pragma unused(aStream) 

assert(aStream == self.networkStream); 



switch (eventCode) { 

    case NSStreamEventOpenCompleted: { 

     [self updateStatus:@"Opened connection"]; 

    } break; 

    case NSStreamEventHasBytesAvailable: { 

     assert(NO);  // should never happen for the output stream 

    } break; 

    case NSStreamEventHasSpaceAvailable: { 

     [self updateStatus:@"Sending"]; 



     // If we don't have any data buffered, go read the next chunk of data. 



     if (self.bufferOffset == self.bufferLimit) { 

      NSInteger bytesRead; 



      bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize]; 



      if (bytesRead == -1) { 

       [self stopSendWithStatus:@"File read error"]; 

      } else if (bytesRead == 0) { 

       [self stopSendWithStatus:nil]; 

      } else { 

       self.bufferOffset = 0; 

       self.bufferLimit = bytesRead; 

      } 

     } 



     // If we're not out of data completely, send the next chunk. 



     if (self.bufferOffset != self.bufferLimit) { 

      NSInteger bytesWritten; 

      bytesWritten = [self.networkStream write:&self.buffer[self.bufferOffset] maxLength:self.bufferLimit - self.bufferOffset]; 

      assert(bytesWritten != 0); 

      if (bytesWritten == -1) { 

       [self stopSendWithStatus:@"Network write error"]; 

      } else { 

       self.bufferOffset += bytesWritten; 

      } 

     } 

    } break; 

    case NSStreamEventErrorOccurred: { 

     [self stopSendWithStatus:@"Stream open error"]; 

    } break; 

    case NSStreamEventEndEncountered: { 

     // FOR WHATEVER REASON THIS IS NEVER CALLED!!!! 

    } break; 

    default: { 

     assert(NO); 

    } break; 

} 

} 

答えて

4

は、あなたの質問には、2つの解釈が存在する場合があります。あなたが求めているものが「私はNSOutputStreamを持っていて、それを書き終えました。これをどのように伝えますか?」答えはcloseというメソッドを呼び出すだけで簡単です。

また、実際には「NSInputStreamがあり、ストリームの終わりに達したときを知りたい」場合は、hasBytesAvailableまたはstreamStatus == NSStreamStatusAtEndを参照してください。

実際にステータスNSStreamStatusWritingを取得するには、このスレッドがwrite:maxLength:を呼び出している間に別のスレッドからstreamStatusメソッドを呼び出す必要があります。

---編集:コードの提案

あなたが通知されることはありません飽きないでしょう理由は、(それがFTPストリームではありません固定サイズのストリーム、でない限り)出力ストリームは、決して完成されていないということです。これは入力ストリームで、出力ストリームを閉じることができる時点で「終了」します。それはあなたの元の質問に対する答えです。

さらなる提案として、実行ストリームのエラーを処理する以外は、実行ループスケジューリングと「イベント処理」をスキップします。次に、私は読み取り/書き込みコードをNSOperationサブクラスに入れ、それをNSOperationQueueに送ります。そのキュー内のNSOperationsへの参照を保持することで、それらを簡単にキャンセルし、percentCompleteプロパティを追加することによってプログレスバーを表示することさえできます。私は下のコードをテストして、それが動作します。メモリ出力ストリームをFTP出力ストリームに置き換えます。私はあなたが当然保たなければならない検証をスキップしたことに気付くでしょう。おそらく、NSOperationの外部で実行して、ユーザーに問い合わせを簡単にする必要があります。

@interface NSSendFileOperation : NSOperation<NSStreamDelegate> { 

    NSInputStream *_inputStream; 
    NSOutputStream *_outputStream; 

    uint8_t *_buffer; 
} 

@property (copy) NSString* sourceFilePath; 
@property (copy) NSString* targetFilePath; 
@property (copy) NSString* username; 
@property (copy) NSString* password; 

@end 


@implementation NSSendFileOperation 

- (void) main 
{ 
    static int kBufferSize = 4096; 

    _inputStream = [NSInputStream inputStreamWithFileAtPath:self.sourceFilePath]; 
    _outputStream = [NSOutputStream outputStreamToMemory]; 
    _outputStream.delegate = self; 

    [_inputStream open]; 
    [_outputStream open]; 

    _buffer = calloc(1, kBufferSize); 

    while (_inputStream.hasBytesAvailable) { 
     NSInteger bytesRead = [_inputStream read:_buffer maxLength:kBufferSize]; 
     if (bytesRead > 0) { 
      [_outputStream write:_buffer maxLength:bytesRead]; 
      NSLog(@"Wrote %ld bytes to output stream",bytesRead); 
     } 
    } 

    NSData *outputData = [_outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 
    NSLog(@"Wrote a total of %lu bytes to output stream.", outputData.length); 

    free(_buffer); 
    _buffer = NULL; 

    [_outputStream close]; 
    [_inputStream close]; 
} 

- (void) stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 
{ 
    // Handle output stream errors such as disconnections here 
} 

@end 


int main (int argc, const char * argv[]) 
{ 
    @autoreleasepool { 

     NSOperationQueue *sendQueue = [[NSOperationQueue alloc] init]; 

     NSSendFileOperation *sendOp = [[NSSendFileOperation alloc] init]; 
     sendOp.username = @"test"; 
     sendOp.password = @"test"; 
     sendOp.sourceFilePath = @"/Users/eric/bin/data/english-words.txt"; 
     sendOp.targetFilePath = @"/Users/eric/Desktop/english-words.txt"; 

     [sendQueue addOperation:sendOp]; 
     [sendQueue waitUntilAllOperationsAreFinished]; 
    } 
    return 0; 
} 
+0

こんにちは!私の状況はこれです:inputstreamはファイルを読み込み、outputstreamはそれをftpサーバに書き込みます。入力ストリームのバッファへの書き込みが終了すると、接続が切断されます。しかし、これは問題を引き起こしています。なぜなら、出力ストリームは、接続を閉じるときにバッファ全体をftpに書き込まなかったからです。だから私は、ファイルの終わりを切り刻むことなく接続を閉じることができるようにoutputstreamが書き込みを終了したときを知る必要があります。ありがとう! – sharkyenergy

+0

問題は、入力ストリームを閉じるのではなく、データを読み込んだバッファを解放することです(入力ストリームとバッファは2つの異なるオブジェクトです)。入力を読み終えたら、すべてのデータをバッファにコピーして、入力ストリームを閉じても構いません。出力ストリームへの書き込みが終了した後でのみ、バッファを解放してください。 – aLevelOfIndirection

+1

問題を作成する出力ストリームを閉じます。私は接続を常に開いているので、私は傾けることができない "tryPersistentConnection"を無効にしなければならなかった。ファイルをアップロードした後、出力ストリームを閉じる必要があります。問題は、私はoutputstreamがその作業を終える時を知らないです。私は入力ストリームが終了したときしか知りません。 – sharkyenergy

関連する問題