2013-10-12 12 views
7

私は単純なcソケットサーバーに接続するNSStreamsを使ってチャットアプリケーションを作っています。ストリームが正常に接続され、データが送信されますが、データを受信できません。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 

問題は、それがハングアップということです。ここではNSStreamsを使用して、私のSocketクラスでありますこの1行のコードは永遠に:while (![inputStream hasBytesAvailable]) {}ですが、私はなぜそれがわかりません。サーバーはメッセージを送り返す必要があります。

+0

私は同じ問題に直面しています...どのような解決策ですか? –

答えて

4

したがって、NSStreamDelegateを見ると、そのswitch文のすべてのケースを実装していないようです。私は最近、非常に似たような方法でNSStreamNSStreamDelegateを使っているOS X用のIRCクライアントを書いています。そして、すべてのケースをチェックしていないときにコンパイラが不平を言うのは間違いありません。

あなたが例

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred
をチェックする必要があるように見える some of my codeに振り返ります

あなたがチェックしていない場合は、NSStreamEventHasSpaceAvailableです。ストリームに書き込むことができます。

編集:あなたのコードをもう一度読むと、代理人の代わりにoutputStreamオブジェクトを使用していると表示され、inputStreamから読み取る作業を自分で行っています。私はあなたがおそらくデリゲートを使いたいと思うし、コードがネットワークからどのようにデータを受け取るかを大幅に簡素化するので、あなたの入力ストリームから直接読むことはありません。私が理解するところでは、NSStreamは、データがネットワークからバッファされるという事実の周りに小さな抽象レイヤーを提供するため、のようなことをする必要はありません。

edit2:あなたのコードについての最新情報をwhile (![inputStream hasBytesAvailable])にすることはありません。あなたのストリームを正しく使用していないということはかなり明らかです。私が見る方法では、NSStreamを使用する最良の方法は、そのhandleEvent:(NSStreamEvent) eventメソッドを使ってイベントに応答し、バイトを書き込むように直接指示したり、使用可能なバイトがあるまでスリープしたりしないことです。

私はあなたにリンクしているコードで、readDelegateとwriteDelegateの両方がNSStreamsを処理しているので、writeDelegate hereの使い方を見てみてください。私は基本的には、addCommand:(NSString *) commandというメソッドを用意しています。このメソッドはストリームをキューに書き込むための文字列を入れて、次にストリームデリゲートがバイト(NSStreamEventHasSpaceAvailable)を書き込むことができたら、できるだけ多くのバイトを書き込みます。私はこれが助けて欲しい!

+0

お返事ありがとうございます。私が入力ストリームを持っている理由は、利用可能なバイトがない場合にnilを返さないようにするためです。返すものがあるまでループします。面白いのは、if(isLoggedIn)行をコードから取り除くと、更新UIのコードがデータの送受信の両方で完全に機能することです。私はあなたの提案を検討しており、それがうまくいくことを願っています。 – jamespick

+0

代わりに 'NSStreamHasSpaceAvailable'がありますが、 'NSStreamEventHasSpaceAvailable'はありません。 –

関連する問題