NSStream无法接收数据
我正在使用连接到简单c socket服务器的NSStream进行聊天应用程序。流成功连接并发送数据,但无法接收数据。下面是一个使用NSStreams我的Socket类:NSStream无法接收数据
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
的问题是,它挂在这一行代码永远: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:我读了你的关于你的代码的更新,永远不会通过while (![inputStream hasBytesAvailable])
,它似乎很清楚,问题是你没有正确使用你的流。我看到它的方式,使用NSStream
的最好方法是使用它的handleEvent:(NSStreamEvent) event
方法来响应事件,并且从不直接告诉它写入字节,或者直到它有可用字节时才进入休眠状态。
在我链接到你的代码中,我有一个readDelegate和一个writeDelegate,它们都处理NSStreams
,你可能想看看我如何使用我的writeDelegate here。我基本上有一个方法,addCommand:(NSString *) command
,把一个字符串写入到队列中,然后当我的流代理可以写入字节(NSStreamEventHasSpaceAvailable
),我写尽可能多的字节,我可以。我希望这有帮助!
感谢您的回复。我有输入流这样做的原因是因为如果没有字节可用,我不希望它返回零,所以我循环直到它有一些返回。有趣的是,当我将'if(isLoggedIn)'行从代码中取出时,更新UI中的代码完美工作,无论是发送还是接收数据。我正在研究你的建议,我希望它能起作用。 – jamespick
没有什么'NSStreamHasSpaceAvailable',而是'NSStreamEventHasSpaceAvailable'。 –
我面临同样的问题...任何解决方案? –