代码之家  ›  专栏  ›  技术社区  ›  jamespick

NSStream无法接收数据

  •  7
  • jamespick  · 技术社区  · 11 年前

    我正在使用连接到一个简单的c套接字服务器的NSStreams制作一个聊天应用程序。流连接成功,并发送数据,但无法接收数据。这是我的使用NSStreams的Socket类:

    插座.h

    @interface Socket : NSObject <NSStreamDelegate>
    
    - (void)connectToServerWithIP:(NSString *)ip andPort:(int)port;
    - (NSString *)sendMessage:(NSString *)outgoingMessage;
    
    @end
    

    插座.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
    

    聊天视图控制器.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]) {} ,但我不知道为什么。服务器应该发回一条消息。

    1 回复  |  直到 11 年前
        1
  •  4
  •   tjarratt    10 年前

    所以,看看你 NSStreamDelegate ,看起来您还没有实现switch语句的所有情况。我最近为OSX编写了一个IRC客户端,它使用 NSStream NSStream委派 以非常相似的方式,我非常确信,当你没有检查所有的情况时,编译器应该会抱怨。

    回顾 some of my code 看起来你应该检查一下这些箱子

    • NSStreamEventHasSpaceAvailable
    • NSStreamEventOpenCompleted
    • NSStreamEventHasBytesAvailable
    • NSStreamEventEndEncountered
    • NSStreamEventErrorOccurred

    所以你没有检查的情况是 NSStream事件具有可用空间 ,此时您可以开始向流中写入内容。

    编辑:再次阅读您的代码,我在您的 sendMessage 您正在使用的操作 outputStream 对象,而不是委托编写,然后自己完成从 inputStream 。我认为您可能希望使用委托,而不要直接从输入流中读取,因为这将极大地简化代码从网络接收数据的方式。据我所知, NSStream(NS流) 有没有围绕数据是从网络缓冲的这一事实提供一个小的抽象层,这样你就不需要做调用之类的事情 usleep 而您的输入流没有可供读取的字节。

    编辑2:我读到了你关于你的代码永远不会过时的更新 while (![inputStream hasBytesAvailable]) 很明显,问题在于您没有正确使用流。在我看来,最好的使用方式 NSStream(NS流) 是使用其 handleEvent:(NSStreamEvent) event 方法,并且永远不要直接告诉它写入字节,或者在有可用字节之前休眠。

    在我链接到您的代码中,我有一个readDelegate和一个writeDelegate,它们都处理 NSStreams ,您可能想看看我是如何使用writeDelegate的 here 。我基本上有一个方法, addCommand:(NSString *) command 将要写入流的字符串放入队列,然后当我的流委托可以写入字节时( NSStream事件具有可用空间 ),我写尽可能多的字节。我希望这能有所帮助!