2013-10-12 6 views
7

Ich mache eine Chat-Anwendung mit NStreams, die mit einem einfachen c-Socket-Server verbinden. Der Datenstrom verbindet sich erfolgreich und sendet Daten, aber er kann die Daten nicht empfangen. Hier ist meine Socket-Klasse, die NSStreams verwendet:NStream schlägt fehl, Daten zu empfangen

socket.h

@interface Socket : NSObject <NSStreamDelegate> 

- (void)connectToServerWithIP:(NSString *)ip andPort:(int)port; 
- (NSString *)sendMessage:(NSString *)outgoingMessage; 

@end 

Socket.m

#import "Socket.h" 

@interface Socket() 

@property (strong, nonatomic) NSInputStream *inputStream; 
@property (strong, nonatomic) NSOutputStream *outputStream; 
@property (strong, nonatomic) NSString *output; 

@end 

@implementation Socket 

@synthesize inputStream; 
@synthesize outputStream; 
@synthesize output; 

- (void)connectToServerWithIP:(NSString *)ip andPort:(int)port 
{ 
    CFReadStreamRef readStream; 
    CFWriteStreamRef writeStream; 
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)ip, port, &readStream, &writeStream); 
    inputStream = (__bridge_transfer NSInputStream *)readStream; 
    outputStream = (__bridge_transfer NSOutputStream *)writeStream; 
    [inputStream setDelegate:self]; 
    [outputStream setDelegate:self]; 
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [inputStream open]; 
    [outputStream open]; 
} 

- (NSString *)sendMessage:(NSString *)outgoingMessage 
{ 
    NSData *messageData = [outgoingMessage dataUsingEncoding:NSUTF8StringEncoding]; 
    const void *bytes = [messageData bytes]; 
    uint8_t *uint8_t_message = (uint8_t*)bytes; 
    [outputStream write:uint8_t_message maxLength:strlen([outgoingMessage cStringUsingEncoding:[NSString defaultCStringEncoding]])]; 
    while (![inputStream hasBytesAvailable]) { 
     usleep(10); 
    } 
    uint8_t buffer[1024]; 
    [inputStream read:buffer maxLength:1023]; 
    NSString *outputString = [NSString stringWithUTF8String:(char *)buffer]; 
    return outputString; 
} 

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { 
    NSLog(@"Stream Event: %lu", streamEvent); 

    switch (streamEvent) { 
     case NSStreamEventOpenCompleted: 
      NSLog(@"Stream opened"); 
      break; 
     case NSStreamEventHasBytesAvailable: 
      if (theStream == inputStream) { 
       uint8_t buffer[1024]; 
       long len; 
       while ([inputStream hasBytesAvailable]) { 
        len = [inputStream read:buffer maxLength:sizeof(buffer)]; 
        if (len > 0) { 
         output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; 
         if (output) { 
          NSLog(@"Data: %@", output); 
         } 
        } 
       } 
      } 
      break; 
     case NSStreamEventErrorOccurred: 
      NSLog(@"Can not connect to the host!"); 
      break; 
     case NSStreamEventEndEncountered: 
      [theStream close]; 
      [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
      theStream = nil; 
      break; 
     default: 
      NSLog(@"Unknown event"); 
    } 
} 

@end 

ChatViewController.m

// 
// ChatViewController.m 
// Chat 
// 
// Created by James Pickering on 10/5/13. 
// Copyright (c) 2013 James Pickering. All rights reserved. 
// 

#import "ChatViewController.h" 
#import "LoginViewController.h" 
#import "StatusView.h" 

@interface ChatViewController() 

@property (strong) IBOutlet NSTableView *people; 
@property (strong) IBOutlet NSTextField *message; 
@property (strong) IBOutlet NSButton *send; 
@property (strong) IBOutlet NSButton *loginButton; 
@property (strong) IBOutlet NSButton *settingsButton; 
@property (strong) IBOutlet NSButton *panicButton; 

@property (strong, nonatomic) NSString *recievedText; 
@property (strong, nonatomic) NSMutableArray *tableData; 
@property (strong, nonatomic) NSInputStream *inputStream; 
@property (strong, nonatomic) NSOutputStream *outputStream; 


- (void)openChat:(id)sender; 

- (IBAction)panic:(id)sender; 
- (IBAction)loginToChat:(id)sender; 

@end 

@implementation ChatViewController 

@synthesize sock; 
@synthesize recievedText; 
@synthesize inputStream; 
@synthesize outputStream; 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 
{ 
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 
    if (self) { 
     self.isLoggedIn = FALSE; 
     sock = [[Socket alloc] init]; 
     [sock connectToServerWithIP:@"127.0.0.1" andPort:5001]; 
     //[self updateUI]; 
    } 
    return self; 
} 

- (void)updateUI 
{ 
    if (self.isLoggedIn) { 
     recievedText = [sock sendMessage:@"getPeople"]; 
     self.tableData = [[NSMutableArray alloc] initWithArray:[recievedText componentsSeparatedByString:@";"]]; 
     NSLog(@"%@", self.tableData); 
     [self.people reloadData]; 
    } 
} 

- (void)openChat:(id)sender 
{ 
    NSLog(@"tru"); 
} 

- (IBAction)panic:(id)sender { 

} 

- (IBAction)loginToChat:(id)sender { 
    NSLog(@"Called"); 
    if (self.loginPopover == nil) { 
     NSLog(@"Login Popover is nil"); 
     self.loginPopover = [[NSPopover alloc] init]; 
     self.loginPopover.contentViewController = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil]; 
    } 
    if (!self.loginPopover.isShown) { 
     NSLog(@"Login Popover is opening"); 
     [self.loginButton setTitle:@"Cancel"]; 
     [self.settingsButton setEnabled:NO]; 
     [self.send setEnabled:NO]; 
     [self.message setEnabled:NO]; 
     [self.loginPopover showRelativeToRect:self.loginButton.frame ofView:self.view preferredEdge:NSMinYEdge]; 
    } 
    else { 
     NSLog(@"Login Popover is closing"); 
     [self.loginButton setTitle:@"Login"]; 
     [self.settingsButton setEnabled:YES]; 
     [self.send setEnabled:YES]; 
     [self.message setEnabled:YES]; 
     [self.loginPopover close]; 
    } 
} 

- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView 
{ 
    return [self.tableData count]; 
} 

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex 
{ 
    return [self.tableData objectAtIndex:rowIndex]; 
} 

- (BOOL)canBecomeKeyWindow 
{ 
    return YES; 
} 

- (BOOL)loginWithUsername:(NSString *)username andPassword:(NSString *)password 
{ 
    // Error happens here 

    recievedText = [sock sendMessage:@"login"]; 
    if ([recievedText isEqualToString:@"roger"]) { 
     recievedText = [sock sendMessage:[NSString stringWithFormat:@"%@;%@", username, password]]; 
     if ([recievedText isEqualToString:@"access granted"]) { 
      return YES; 
     } 
     else { 
      return NO; 
     } 
    } 
    else { 
     return NO; 
    } 
} 

@end 

Das Problem ist, dass es hängt an diese eine Zeile Code für immer: while (![inputStream hasBytesAvailable]) {}, aber ich habe keine Ahnung warum. Der Server sollte eine Nachricht zurücksenden.

+0

Ich habe das gleiche Problem ... irgendeine Lösung? –

Antwort

4

Also, Blick auf Ihre NSStreamDelegate, es sieht aus wie Sie nicht alle Fälle für diese switch-Anweisung implementiert haben. Ich habe kürzlich einen IRC-Client für OS X geschrieben, der NSStream und NSStreamDelegate auf sehr ähnliche Weise verwendet, und ich bin ziemlich sicher, dass der Compiler sich beschweren sollte, wenn Sie nicht alle Fälle dort überprüft haben.

Im Rückblick auf some of my code es sieht aus wie Sie für die Fälle

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred
prüft werden sollte,

Also der Fall, den Sie nicht überprüft haben, ist NSStreamEventHasSpaceAvailable, das ist, wenn Sie anfangen können, in Ihren Stream zu schreiben.

edit: Code Lesen wieder, sehe ich in Ihrer sendMessage Aktion, die Sie mit dem outputStream Objekt anstelle des Delegierten zu schreiben, und dann die Arbeit machen Sie sich von der inputStream zu lesen. Ich denke, dass Sie wahrscheinlich den Delegaten verwenden möchten und niemals direkt von Ihrem Eingabestream lesen, da dies wesentlich vereinfacht, wie Ihr Code Daten aus dem Netzwerk empfängt. Von dem, was ich verstehe, NSStream gibt es eine kleine Abstraktionsebene um die Tatsache, dass Daten aus dem Netzwerk gepuffert ist, so dass Sie Dinge wie Anruf usleep nicht tun müssen, während Ihr Eingangsstrom nicht über Bytes zum Lesen verfügt.

edit2: Ich habe Ihr Update über Ihren Code nie über while (![inputStream hasBytesAvailable]) gelesen und es scheint ziemlich klar, dass das Problem ist, dass Sie nicht Ihre Streams richtig verwenden. So wie ich es sehe, ist der beste Weg, um NSStream zu verwenden, mit der Methode handleEvent:(NSStreamEvent) event auf Ereignisse zu reagieren und es niemals direkt zu schreiben, Bytes zu schreiben oder zu schlafen, bis es Bytes zur Verfügung hat.

Im Code, den ich Ihnen verbunden sind, habe ich eine readDelegate und ein writeDelegate, die beide NSStreams behandeln, könnten Sie einen Blick auf nehmen wollen, wie ich meine here writeDelegate verwenden. Ich habe im Grunde eine Methode, addCommand:(NSString *) command, die eine Zeichenfolge in den Stream in eine Warteschlange schreiben schreibt, und wenn mein Stream-Delegat Byte schreiben kann (NSStreamEventHasSpaceAvailable), schreibe ich so viele Bytes wie ich kann. Ich hoffe das hilft!

+0

Danke für Ihre Antwort. Der Grund dafür, dass der Eingabestream das tut, ist, weil ich nicht will, dass es null zurückgibt, wenn keine Bytes verfügbar sind, also mache ich eine Schleife, bis es etwas zurückgibt. Das Lustige daran ist, dass wenn ich die Zeile 'if (isLoggedIn)' aus dem Code herausnehme, der Code in der Update-UI perfekt funktioniert, sowohl beim Senden als auch beim Empfangen von Daten. Ich untersuche Ihren Vorschlag und ich hoffe, es funktioniert. – jamespick

+0

Es gibt nichts "NSStreamHasSpaceAvailable" statt "NSStreamEventHasSpaceAvailable". –