2013-04-28 14 views
8

NSOutputStream डेटा भेजने के समाप्त होने पर कनेक्शन बंद कैसे हो सकता है?एनएसओटपुटस्ट्रीम समाप्त होने पर कनेक्शन बंद करें

चारों ओर खोज करने के बाद मुझे पता चला है कि ईवेंट NSStreamEventEndEncountered केवल तभी कॉल किया जाता है जब सर्वर कनेक्शन छोड़ देता है। अगर आउटपुटस्ट्रीम ने डेटा भेजने के लिए समाप्त नहीं किया है।

StreamStatus हमेशा 0 (कनेक्शन बंद) या 2 (कनेक्शन खुला) लौटा रहा है लेकिन कभी नहीं 4 (डेटा लिखना)।

क्योंकि ऊपर वर्णित दोनों विधियां मुझे लिखने की प्रक्रिया के बारे में पर्याप्त नहीं बता रही हैं, मैं यह निर्धारित करने में सक्षम नहीं हूं कि स्ट्रीम अभी भी लिख रहा है या यदि यह समाप्त हो गया है और मैं अब कनेक्शन बंद कर सकता हूं।

Googleling के 5 दिनों के बाद और कोशिश कर रहा हूं कि मैं पूरी तरह से विचारों से बाहर हूं ... किसी भी मदद की सराहना की। धन्यवाद

संपादित जोड़ा कोड का अनुरोध किया के रूप में:

- (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

आपके सवाल का दो व्याख्याएं हो सकते हैं। यदि आप जो पूछ रहे हैं वह है "मेरे पास एक NSOutputStream है और मैं इसे लिखना समाप्त कर रहा हूं, मैं इसे कैसे सिग्नल करूं?" तो उत्तर उस पर close विधि को कॉल करने के समान सरल है।

वैकल्पिक रूप से, यदि आप वास्तव में कह रहे हैं कि "मेरे पास एनएसआईएनपुट स्ट्रीम है और मैं जानना चाहता हूं कि मैं अंत-स्ट्रीम में कब पहुंच गया हूं" तो आप hasBytesAvailable या streamStatus == NSStreamStatusAtEnd देख सकते हैं।

आपकी जानकारी के लिए, वास्तव में स्थिति NSStreamStatusWriting प्राप्त करने के लिए आपको स्ट्रीम थ्रेड से दूसरी थ्रेड से स्ट्रीमस्टैटस विधि को कॉल करने की आवश्यकता होगी, जबकि यह थ्रेड write:maxLength: पर कॉल कर रहा है।

--- संपादित करें: कोड सुझाव

कारण आपको सूचित कभी नहीं होगा कि एक निर्गम धारा है कभी नहीं समाप्त (जब तक यह एक निश्चित आकार धारा है, जो एक FTP धारा नहीं है)। यह इनपुट स्ट्रीम है जो "समाप्त" हो जाती है जिस बिंदु पर आप अपनी आउटपुट स्ट्रीम बंद कर सकते हैं। यह आपके मूल प्रश्न का उत्तर है।

एक और सुझाव के रूप में, मैं आउटपुट स्ट्रीम पर त्रुटियों को संभालने के अलावा रन लूप शेड्यूलिंग और "ईवेंट प्रोसेसिंग" को छोड़ दूंगा। फिर मैं एनएसओपरेशन सबक्लास में पढ़/लिखने वाला कोड डालूंगा और उसे NSOperationQueue में भेज दूंगा। उस कतार में NSOperations का संदर्भ रखते हुए आप उन्हें आसानी से रद्द कर पाएंगे और एक प्रतिशत पूर्ण संपत्ति जोड़कर प्रगति पट्टी भी दिखा सकते हैं। मैंने नीचे दिए गए कोड का परीक्षण किया है और यह काम करता है। अपनी मेमोरी आउटपुट स्ट्रीम को अपनी एफ़टीपी आउटपुट स्ट्रीम के साथ बदलें। आप देखेंगे कि मैंने सत्यापन को छोड़ दिया है, जिसे आपको निश्चित रूप से रखना चाहिए। उपयोगकर्ता को क्वेरी करना आसान बनाने के लिए उन्हें शायद एनएसओपरेशन के बाहर किया जाना चाहिए।

@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

हैलो! मेरी स्थिति यह है: इनपुटस्ट्रीम फ़ाइल को पढ़ता है, आउटपुटस्ट्रीम इसे एक FTP सर्वर पर लिखता है। जब इनपुटस्ट्रीम बफर में लिखना समाप्त हो जाता है, तो मैं teh कनेक्शन बंद करता हूं। लेकिन यह एक समस्या पैदा कर रहा है, क्योंकि जब मैं कनेक्शन बंद कर रहा हूं तो आउटपुट स्ट्रीम ने पूरे बफर को ftp पर नहीं लिखा था। तो मुझे यह जानना होगा कि आउटपुटस्ट्रीम ने लेखन समाप्त कर लिया है ताकि मैं फ़ाइल के अंत को बंद किए बिना कनेक्शन बंद कर सकूं। धन्यवाद! – sharkyenergy

+0

समस्या तब नहीं है जब आप इनपुट स्ट्रीम बंद करते हैं लेकिन आप उस बफर को छोड़ देते हैं जिसमें आपने अपना डेटा पढ़ा है (इनपुट स्ट्रीम और बफर दो अलग-अलग ऑब्जेक्ट्स हैं)।यदि आपने इनपुट पढ़ना समाप्त कर लिया है तो आपने अपने डेटा को अपने बफर में कॉपी किया है, इसलिए इनपुट स्ट्रीम को बंद करना ठीक है। आउटपुट स्ट्रीम में लिखने के बाद केवल बफर को छोड़ दें। – aLevelOfIndirection

+1

आउटपुट स्ट्रीम को बंद करने से समस्याएं उत्पन्न होती हैं। मुझे "tryPersistentConnection" को अक्षम करना पड़ा क्योंकि मैं कनेक्शन को लगातार खोल नहीं सकता था। इसलिए जब मैं फ़ाइल अपलोड करता हूं तो मुझे आउटपुटस्ट्रीम बंद करना होगा। समस्या यह नहीं है कि आउटपुटस्ट्रीम अपना काम पूरा कर लेता है। मुझे केवल तब पता है जब इनपुट स्ट्रीम समाप्त हो जाती है .. – sharkyenergy

संबंधित मुद्दे