2013-10-12 18 views
7

Basit bir c soket sunucusuna bağlanan NSStreams kullanarak bir sohbet uygulaması yapıyorum. Akış başarıyla bağlanır ve veri gönderir, ancak verileri alamaz.NSStream Verileri Alma Başarısız Olması

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 

sorun üzerinde asılı olmasıdır: Burada NSStreams kullanan benim Soket sınıftır Sonsuza kadar bu kod satırı: while (![inputStream hasBytesAvailable]) {}, ama neden olduğunu bilmiyorum. Sunucu bir mesaj gönderiyor olmalı.

+0

zaman ... her türlü çözüme aynı sorunla karşı karşıya mı? –

cevap

4

Yani, NSStreamDelegate'a bakarak, bu anahtar deyimi için tüm vakaları uygulamadığınız görülüyor. Geçenlerde çok benzer bir şekilde NSStream ve NSStreamDelegate kullanan OS X için bir IRC istemcisi yazdım ve derleyicinin orada tüm durumları kontrol etmediğinizde şikayet etmesi gerektiğinden eminim.

Eğer durumlarda

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred
kontrol edilmelidir gibi görünüyor some of my code geri Looking

Bu nedenle, kontrol etmediğiniz durum NSStreamEventHasSpaceAvailable, yani akışınıza yazmaya başlayabileceğiniz zamandır.

düzenleme: Yeniden kodunuzu okuma, Ben kendine inputStream okuma işi daha sonra yazmaya temsilci yerine outputStream nesnesini kullanıyor ve senin sendMessage eylem görüyoruz. Muhtemelen temsilci kullanmak ve doğrudan giriş akışınızdan okumak istemediğinizi düşünüyorum, çünkü kodunuzun ağdan veri almasını büyük ölçüde kolaylaştıracaktır. Anladığım kadarıyla, usleep gibi şeyler yapmanıza gerek kalmadan, giriş akışınızın okunması için bayt içermediğinden, verilerin ağdan arabelleğe alındığı gerçeği hakkında küçük bir özet katmanı sağlamak için NSStream var.

edit2: while (![inputStream hasBytesAvailable]) kodunun hiçbir zaman geçmediğini anlatan kodunuzu okudum ve sorunun akışınızı doğru kullanmıyor olmanız oldukça açık görünüyor. Gördüğüm kadarıyla, NSStream'u kullanmanın en iyi yolu, olayları handleEvent:(NSStreamEvent) event yöntemini kullanarak yanıtlamaktır ve hiçbir zaman doğrudan byte yazmasını söyleme veya baytları bulunana kadar uyumaktır.

Size bağlandığım kodda, her ikisi de NSStreams'u işleyen bir readDelegate ve writeDelegate var, benim writeDelegate here'u nasıl kullandığımı incelemek isteyebilirsiniz. Temel olarak, bir sıraya akışa yazmak için bir dize koyan bir addCommand:(NSString *) command yöntemine sahibim ve sonra delege temsilcisim bayt yazabiliyorsa (NSStreamEventHasSpaceAvailable), olabildiğince çok bayt yazarım. Umarım bu yardımcı olur!

+0

Yanıt verdiğiniz için teşekkür ederiz. Giriş akışına sahip olmamın sebebi, eğer bayt bulunmazsa, geri dönmesini istemememdir, bu yüzden dönecek bir şey olana kadar dönüyorum. Komik olan şey, 'if (isLoggedIn)' satırını koddan çıkardığımda, güncelleme UI'sindeki kodun hem veri gönderip hem de göndererek mükemmel şekilde çalışmasıdır. Senin önerine bakıyorum ve umarım işe yarıyor. – jamespick

+0

Hiçbir şey 'NSStreamHasSpaceAvailable' yerine 'NSStreamEventHasSpaceAvailable' var. –

İlgili konular