I have an iPhone app that is a client for an existing server application.
I am using the following code to connect, and this code was working fine. Today I started work on the project, and something odd is happening.
When I click "Connect" NSHost takes about 30-45 seconds to resolve and connect to the host. Once the connection is established, data transfer speeds are normal and fast.
I have the original client software (PC Application) connecting to the same server, and the connection is handled immediately!!
Perhaps someone can shed some light on the subject...
-(IBAction)tryConnection{
[self showLogoffButtons];
iPhone_PNPAppDelegate *mainDelegate = (iPhone_PNPAppDelegate *)[[UIApplication sharedApplication] delegate];
settings=mainDelegate.settings;
[activityindicator startAnimating];
sendState=0;
NSHost* host;
//30-45 second pause happens here (since I am using an IP Address)
host = [NSHost hostWithAddress:settings.masterLocation];
//NSHost returns (eventually) with the correct connection.
if (!host) {
host = [NSHost hostWithName:settings.masterLocation];
}
if( host ) {
[NSStream getStreamsToHost:host port:[settings.masterPort intValue] inputStream:&iStream outputStream:&oStream] ;
if( nil != iStream && nil != oStream ) {
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[iStream open];
[oStream open];
}else{
[self resetConnectionAndScreen];
self.navigationItem.prompt=#"Connection Error! Please check connection settings.";
}
}else{
[self resetConnectionAndScreen];
self.navigationItem.prompt=#"Connection Error! Please check connection settings.";
}
}
//I am also now getting these warnings.**.. wth?! :)
warning: no '+hostWithAddress:' method found
warning: (Messages without a matching method signature
warning: no '+hostWithName:' method found
warning: 'NSStream' may not respond to '+getStreamsToHost:port:inputStream:outputStream:'
I have updated my code to the following, which has resolved my issue.
-(IBAction)tryConnection{
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)settings.masterLocation, [settings.masterPort intValue], &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
iStream = (NSInputStream *)readStream;
[iStream retain];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)writeStream;
[oStream retain];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream open];
}
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream);
}
here is another way of networking using cfsockets
http://randomsnippetsofusefulstuff.blogspot.com/
Related
I have implemented socket connection in my project and it is working fine.
But my problem is that I want to send a string request on opening completion of socket.
Delegate is working fine.
- (void)initNetworkCommunication
{
lastString=#"";
isSocketAuthorized=NO;
NSLog(#"$$$$$$$$$$$$$$$$$$$$$$$$$============socket connection INITIALISED....");
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,(CFStringRef)#"XXX.XX.XXX.XX",XXXX, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventOpenCompleted:{
NSLog(#"Stream opened");
[self performSelectorInBackground:#selector(sendAuthRequest) withObject:nil];
}
break;
case NSStreamEventHasBytesAvailable:
{
if (aStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
// NSLog(#"server said: %#", output);
[self messageReceived:output];
}
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
{
[aStream close];
[aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
break;
default:
NSLog(#"Unknown event");
}
}
But stream open is called twice.Why so? can anyone help me.
Thanks.
After a long time of spending my brain over the problem , I come to know that I am actually calling open stream two times.First time for Input Stream and second time for Output Stream.And at last I got the solution to fire my method once in a no of event called,I used condition for NSStream class kind.
case NSStreamEventOpenCompleted:{
if ([aStream isKindOfClass:[inputStream class]]) {
NSLog(#"Input stream opened");
}else{
NSLog(#"output stream opened");
[self performSelectorInBackground:#selector(sendAuthRequest) withObject:nil];
}
}
OK. So there is an exceptional case I have to handle for NSStreamDelegate implementation.
The process is the following:
1- a COMMAND data is written to the output stream to trigger the other end
2- The other end, starts putting data for the input stream BUT asynchronously, meaning that the first part of the data is received at the beginning then a couple of seconds later, the rest of the data comes in.
3- After all the data is received, the server will NOT send any data but the connection remains open. (input stream status = Open (2))
4- After couple of minutes, the server ends the connection (input stream status = 5) and NSStreamEventEndEncountered is triggered.
SendRequest class
- (void)sendSecondRequest {
CFReadStreamRef readStream = nil;
CFWriteStreamRef writeStream = nil;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.SOMEServer, self.SOMEPort, &readStream, &writeStream);
self.inputStream = (__bridge NSInputStream *)readStream;
self.outputStream = (__bridge NSOutputStream *)writeStream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
NSData *data = [NSData dataWithData:[kSecondCall dataUsingEncoding:NSASCIIStringEncoding]];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
[self.outputStream write:[data bytes] maxLength:[data length]];
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
if (aStream == self.inputStream) {
uint8_t buffer[1024];
NSInteger len = [self.inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
[gOutput appendString:output];
}
}
break;
case NSStreamEventEndEncountered:
NSLog(#"Can not connect to the host");
[aStream close];
[aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
aStream = nil;
break;
default:
break;
}
}
- (void)disconnectSocket {
[self.inputStream close];
[self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream close];
[self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream setDelegate:nil];
[self.outputStream setDelegate:nil];
self.inputStream = nil;
self.outputStream = nil;
}
The gOutput variable is a global variable that I use to read and store input stream.
The problem is that since the data's first part comes immediately and then the rest comes in asynchronously, I can not handle the moment when there is no more data available.
In other words, how can I know if the server is done sending data and I should start processing it?
Server sends a terminator sequence at the end. when the client detects this terminator, it starts to process....
I use the following code to connect to server and handle events.
almost copy-paste of http://www.devx.com/wireless/Article/43551
I want to force close the stream before NSStreamEventEndEncountered.
Documentation is lacking and having hard time to figure out how to force close the streams associated with connection. (or close underlying socket if I have to)
Thank you
-(void) connectToServerUsingStream:(NSString *)urlStr
portNo: (uint) portNo {
if (![urlStr isEqualToString:#""]) {
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(#"%# is not a valid URL");
return;
} else {
[NSStream getStreamsToHostNamed:urlStr
port:portNo
inputStream:&iStream
outputStream:&oStream];
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
[iStream open];
}
}
}
and handles event
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"stream event %d", eventCode) ;
if( stream == iStream ) NSLog(#"on input stream");
else if( stream == oStream ) NSLog(#"on output stream");
else NSLog(#"on unknown stream identifier") ;
switch(eventCode) {
case NSStreamEventEndEncountered:
{
NSLog(#"stream ended; will be closed") ;
[stream close];
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[stream release];
stream = nil; // stream is ivar, so reinit it
break;
}
case NSStreamEventErrorOccurred:
NSLog(#"stream error") ;
break ;
case NSStreamEventHasBytesAvailable:
//TODO: read here
break ;
case NSStreamEventNone:
NSLog(#"stream null event") ;
break ;
case NSStreamEventOpenCompleted:
NSLog(#"stream is now open") ;
break ;
case NSStreamEventHasSpaceAvailable:
//write here
break ;
}
}
Execute the following method on the thread/runloop that the stream was scheduled on:
- (void)closeStream:(NSStream *)stream {
[stream close];
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[stream release];
stream = nil;
}
I am using tcp connection to connect a server using ip and port. I can write and read stream on it. my problem is when I turn off server .app stop with "EXC_BAD_ACCESS" can anyone help me?
this is connect code:
-(void) connectToServerUsingStream:(NSString *)urlStr
portNo: (uint) portNo {
if (![urlStr isEqualToString:#""])
{
NSURL *website = [NSURL URLWithString:urlStr];
if (!website)
{
NSLog(#"%# is not a valid URL");
return;
}
else
{
[NSStream getStreamsToHostNamed:urlStr
port:portNo
inputStream:&iStream
outputStream:&oStream];
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
[iStream open];
}
}
}
and this is stream event delegate:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if (data == nil)
{
data = [[NSMutableData alloc] init];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len)
{
[data appendBytes:(const void *)buf length:len];
int bytesRead;
bytesRead += len;
}
else
{
NSLog(#"No data.");
return;
}
NSString *str = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"From server: %#",str);
[str release];
[data release];
data = nil;
break;
}
case NSStreamEventErrorOccurred:
{
NSError *theError = [stream streamError];
NSLog(#"Error reading stream! ,Error %i: %#",[theError code], [theError localizedDescription]);
[self disconnect];
[self connectToServerUsingStream:kHostIP portNo:kPort];
break;
}
case NSStreamEventHasSpaceAvailable:
{
if(stream == oStream && !isDataSent)
{
isDataSent = YES;
[self writeToServer:#"HI"];
}
break;
}
}
}
You should probably implement NSStreamEventEndEncountered case and close / release the stream.
What logging messages are you getting so you can see which parts of your code get called?
Have you tried setting some breakpoints and then stepping through to see the exact point of failure and also then inspect the objects in case you aren't retaining/releasing properly?
What other debugging steps have you taken so far?
Try adding the following linker flags:
OTHER_LDFLAGS = -lz -lxml2 -ltidy -ObjC
To your project.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSOutputStream *outputStream;
NSInputStream *inputStream;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
Input,OutputStream delegate didn't call.What i am doing wrong.
Did you run the current run loop by following way ?
do {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
} while (done == TRUE);
You seem to have omitted the streams' initialization, e.g.
NSInputStream *inputStream = [NSInputStream inputStreamWithURL: myURL];
NSOutputStream *outputStream = [NSOutputStream outputStreamToMemory];