objective-c – NSStream无法接收数据

我正在使用连接到简单的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) 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]){},但我不明白为什么.服务器应该发回消息.

解决方法

因此,查看您的NsstreamDelegate,看起来您没有实现该switch语句的所有情况.我最近为OS X写了一个IRC客户端,它以非常类似的方式使用Nsstream和NsstreamDelegate,我很确定当你没有检查那里的所有情况时编译器应该抱怨.

回顾some of my code看起来你应该检查案例

> NsstreamEventHasspaceAvailable
> NsstreamEventOpenCompleted
> NsstreamEventHasBytesAvailable
> NsstreamEventEndEncountered
> NsstreamEventErrorOccurred

因此,您未检查的情况是NsstreamEventHasspaceAvailable,这时您可以开始写入您的流.

编辑:再次读取代码,我在sendMessage操作中看到您正在使用outputStream对象而不是委托来编写,然后自己完成工作以从inputStream中读取.我想您可能想要使用委托,而不是直接从您的输入流中读取,因为它将极大地简化您的代码从网络接收数据的方式.根据我的理解,Nsstream提供了一个小层抽象,围绕数据从网络缓冲的事实,因此您不需要执行诸如调用usleep之类的事情,而输入流没有可用于读取的字节.

edit2:我读到你的代码永远不会过去的更新(![inputStream hasBytesAvailable])并且很明显问题是你没有正确使用你的流.我看到它的方式,使用Nsstream的最好方法是使用handleEvent:(NsstreamEvent)事件方法响应事件,并且永远不会直接告诉它写入字节,或者在它有可用字节之前休眠.

在我链接到你的代码中,我有一个readDelegate和一个writeDelegate都处理Nsstreams,你可能想看看我如何使用我的writeDelegate here.我基本上有一个方法,addCommand:(Nsstring *)命令放将流写入队列的字符串,然后当我的流委托可以写入字节(NsstreamEventHasspaceAvailable)时,我尽可能多地写入字节.我希望这有帮助!

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...