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]){},但我不明白为什么.服务器应该发回消息.
解决方法
回顾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)时,我尽可能多地写入字节.我希望这有帮助!