7

シンプルな c ソケット サーバーに接続する NSStreams を使用してチャット アプリケーションを作成しています。ストリームは正常に接続し、データを送信しますが、データを受信できません。NSStreams を使用する私の Socket クラスは次のとおりです。

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

問題は、この 1 行のコードで永遠にハングアップすることです:while (![inputStream hasBytesAvailable]) {}が、その理由はわかりません。サーバーはメッセージを送り返しているはずです。

4

1 に答える 1

4

したがって、あなたの を見るとNSStreamDelegate、その switch ステートメントのすべてのケースを実装していないようです。私は最近、非常によく似た方法で and を使用する OS X 用の IRC クライアントを作成しました。すべてのケースをチェックしていない場合、コンパイラはエラーを出すはずNSStreamです。NSStreamDelegate

私のコードのいくつかを振り返ってみると、ケースをチェックする必要があるようです

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred

したがって、チェックしていないケースは ですNSStreamEventHasSpaceAvailable。これは、ストリームへの書き込みを開始できるときです。

編集:コードをもう一度読むと、あなたのsendMessageアクションでoutputStreamは、デリゲートの代わりにオブジェクトを使用して書き込み、自分で作業を行ってinputStream. コードがネットワークからデータを受信する方法が大幅に簡素化されるため、おそらくデリゲートを使用し、入力ストリームから直接読み取らないことをお勧めします。私が理解NSStreamしていることから、データがネットワークからバッファリングされるという事実に基づいて、小さな抽象化レイヤーを提供する必要があるのでusleep、入力ストリームに読み取り可能なバイトがない間、呼び出しなどを行う必要はありません。

edit2: あなたのコードが過去while (![inputStream hasBytesAvailable])のものにならないというあなたの更新を読みましたが、ストリームを正しく使用していないことが問題であることは明らかです。私が考える最善の方法NSStreamは、そのメソッドを使用してイベントに応答し、handleEvent:(NSStreamEvent) eventバイトを書き込むように、またはバイトが利用可能になるまでスリープするように直接指示しないことです。

リンクしたコードには、readDelegate と writeDelegate があり、どちらも処理できます。ここでNSStreamswriteDelegate を使用する方法をご覧ください。基本的に、ストリームに書き込む文字列をキューに入れるメソッドがあり、ストリーム デリゲートがバイト ( ) を書き込むことができる場合は、できるだけ多くのバイトを書き込みます。これが役立つことを願っています!addCommand:(NSString *) commandNSStreamEventHasSpaceAvailable

于 2013-10-12T02:53:22.127 に答える