2013-10-12 127 views
7

我正在使用連接到簡單c socket服務器的NSStream進行聊天應用程序。流成功連接併發送數據,但無法接收數據。下面是一個使用NSStreams我的Socket類:NSStream無法接收數據

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 

的問題是,它掛在這一行代碼永遠:while (![inputStream hasBytesAvailable]) {},但我不知道爲什麼。服務器應該發回消息。

+0

我面臨同樣的問題...任何解決方案? –

回答

4

因此,看着你的NSStreamDelegate,看起來你沒有實現該switch語句的所有情況。我最近爲OS X編寫了一個IRC客戶端,它以非常類似的方式使用NSStreamNSStreamDelegate,並且我非常肯定,當您沒有檢查過所有的情況時,編譯器應該抱怨。

回首some of my code它看起來像你應該檢查的情況下

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred

所以你沒有檢查的情況是NSStreamEventHasSpaceAvailable,這是當你可以開始寫入你的流。

編輯:再次讀您的代碼,我看到您的sendMessage行動中,您使用outputStream對象而不是委託書寫,然後自己從inputStream中讀取作業。我想你可能想使用委託,並且從不直接從輸入流中讀取,因爲它將大大簡化代碼從網絡接收數據的方式。據我所知,NSStream可以提供一個小的抽象層,可以從網絡緩衝數據,因此您不需要執行諸如調用usleep之類的操作,而您的輸入流沒有可讀取的字節。

edit2:我讀了你的關於你的代碼的更新,永遠不會通過while (![inputStream hasBytesAvailable]),它似乎很清楚,問題是你沒有正確使用你的流。我看到它的方式,使用NSStream的最好方法是使用它的handleEvent:(NSStreamEvent) event方法來響應事件,並且從不直接告訴它寫入字節,或者直到它有可用字節時才進入休眠狀態。

在我鏈接到你的代碼中,我有一個readDelegate和一個writeDelegate,它們都處理NSStreams,你可能想看看我如何使用我的writeDelegate here。我基本上有一個方法,addCommand:(NSString *) command,把一個字符串寫入到隊列中,然後當我的流代理可以寫入字節(NSStreamEventHasSpaceAvailable),我寫儘可能多的字節,我可以。我希望這有幫助!

+0

感謝您的回覆。我有輸入流這樣做的原因是因爲如果沒有字節可用,我不希望它返回零,所以我循環直到它有一些返回。有趣的是,當我將'if(isLoggedIn)'行從代碼中取出時,更新UI中的代碼完美工作,無論是發送還是接收數據。我正在研究你的建議,我希望它能起作用。 – jamespick

+0

沒有什麼'NSStreamHasSpaceAvailable',而是'NSStreamEventHasSpaceAvailable'。 –