NSStream cannot receive data

I am making a chat application using NSStreams that connect to a simple socket server. The stream connects successfully and sends data, but it cannot receive data. Here is my Socket class that uses NSStreams:

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 

The problem is that it always stays on that one line of code: while (![inputStream hasBytesAvailable]) {} , but I have no idea why. The server should send the message back.

+7
objective-c sockets nsstream
source share
1 answer

So, looking at your NSStreamDelegate , it looks like you have not implemented all the cases for this switch statement. I recently wrote an IRC client for OS X that uses NSStream and NSStreamDelegate very similar way, and I'm sure the compiler should complain if you haven't checked all cases there.

Looking back at part of my code , it looks like you should check cases

  • NSStreamEventHasSpaceAvailable
  • NSStreamEventOpenCompleted
  • NSStreamEventHasBytesAvailable
  • NSStreamEventEndEncountered
  • NSStreamEventErrorOccurred

So, the case that you did not check is NSStreamEventHasSpaceAvailable , that is, when you can start recording to your stream.

edit: after reading my code again, I see in your sendMessage action that you use the outputStream object instead of writing the delegate, and then do the work yourself to read from the inputStream . I think you probably want to use a delegate and never read directly from your input stream, because it will greatly simplify how your code receives data from the network. From what I understand, NSStream should provide a small layer of abstraction around the fact that data is buffered from the network, so you don't need to do things like call usleep , while your input stream does not have bytes available for reading.

edit2: I read your update that your code never passed by while (![inputStream hasBytesAvailable]) , and it seems pretty obvious that the problem is that you are not using your threads correctly. The way I see it, the best way to use NSStream is to respond to events using its handleEvent:(NSStreamEvent) event method and never directly tell it to write bytes or sleep until the number of bytes is available.

In the code I linked to you, I have readDelegate and writeDelegate that handle NSStreams , you can take a look at how I use my writeDelegate. I basically have an addCommand:(NSString *) command method that puts the string to write to the stream in the queue, and then when the stream delegate can write bytes ( NSStreamEventHasSpaceAvailable ), I write as many bytes as I can. Hope this helps!

+4
source share

All Articles