NSSream, frustrating the hell out me!! - MacRumors Forums
Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Jun 9, 2009, 11:14 AM   #1
turner296
macrumors newbie
 
Join Date: Jan 2009
NSSream, frustrating the hell out me!!

Application Outline
I'm trying to build an application that backs up a folder and uploads it to a server, on online backup storage.

The Problem (or at least I think it is...)
I have the HTTP request within a NSData object, and I'm writing it to an NSOutputStream but when its finished writing and the NSInputStream comes into play I get the wrong response from the server. I thought it must be my request data within the NSData object but then I write that to file. I then compared it to a 1,000,000% (must state that this request is DEFINITELY working) working request that I had written to file using a PERL script, compared the two requests and they were IDENTICAL in every way. I even tried the request I outputted from COCOA into the PERL script and it worked perfectly, so I know the problem lies with the coding of my NSOutputStream.

I logged all the data being sent at each buffer when writing to the NSOutputStream and it does go through right till the end of data till there's no more bytes left, so its got to be sending all of it. It just seems to be the response from the server.

Solution???
I dont want to use the NSURL option because it doesn't really allow a delegate to track the progress of the upload, it basically says connection open, recieved and closed. I need help, considering this is main function of my application I can't go any further with my design or coding of my project till this is resolved.

Code
Code:
- (void)start {
	
	NSURL * uploadURL;
	NSHost * uploadHost;
	
	uploadURL = [NSURL URLWithString:uploadServer];
	uploadHost = [NSHost hostWithName:[uploadURL host]];
	
	[NSStream getStreamsToHost:uploadHost
						  port:80
				   inputStream:&inputStream
				  outputStream:&outputStream];
	
	[inputStream retain];
	[outputStream retain];
	
	[inputStream setDelegate:self];
	[outputStream setDelegate:self];
	
	[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
						   forMode:NSDefaultRunLoopMode];
	[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
							forMode:NSDefaultRunLoopMode];

	
	[inputStream open];
	[outputStream open];
	
}
Code:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
	
    //NSLog(@"stream:handleEvent: is invoked...");
	
	if (stream == outputStream) {
		
		//NSLog(@"output stream: active!");
		
		switch (eventCode) {
			case NSStreamEventHasSpaceAvailable:
			{
				
				// get the range of data to send
				// put it into the buffer
				// send it
				// increase the next range
				
				unsigned int data_length = [uploadData length];
				unsigned int length      = ((data_length - byteIndex >= 1024) ? 1024 : (data_length-byteIndex));
				NSRange dataRange        = NSMakeRange(byteIndex, length);
				uint8_t buffer[length];
				
				[uploadData getBytes:buffer
							   range:dataRange];
				
				[uploadedData appendBytes:buffer
								   length:length];
				
				length = (NSInteger)[(NSOutputStream *)stream write:(const uint8_t *)buffer
														  maxLength:(NSUInteger)length];
				
				byteIndex += length;
				
				NSLog(@"Sent %i of %i.", byteIndex, data_length);
				
				break;
			}
			case NSStreamEventErrorOccurred:
			{
				
				NSLog(@"output stream: (ERROR) %@", [[stream streamError] localizedDescription]);
				
				[stream close];
				[stream release];
				
				stream = nil;
				
				break;
			}
			case NSStreamEventEndEncountered:
			case NSStreamEventOpenCompleted:
			default:
				break;
		}
	}
	
	else if ( stream == inputStream ) {
		
		NSLog(@"input stream: active!");
		
		switch (eventCode) {
			case NSStreamEventHasBytesAvailable:
			{
				if( ! responseData ) {
					responseData = [[NSMutableData data] retain];
				}
				
				uint8_t buf[1024];
				unsigned int len = 0;
				
				len = [(NSInputStream *)stream read:buf maxLength:1024];
				
				if (len) {
					[responseData appendBytes:(const void *)buf
									   length:len];
					// bytesRead is an instance variable of type NSNumber.
					[bytesDownloaded setIntValue:[bytesDownloaded intValue]+len];
				} else {
					NSLog(@"no buffer!");
				}
				break;
			}
			case NSStreamEventEndEncountered:
			{
				
				NSLog(@"input stream: finished downloading");
				
				NSString * str = [[NSString alloc] initWithData:responseData
													   encoding:NSISOLatin1StringEncoding];
				
				NSLog(@"%@", str);
				
			}
				
			default:
				break;
		}
		
	}
}

Thanks for any help in advance
turner296 is offline   0 Reply With Quote
Old Jun 9, 2009, 12:12 PM   #2
Krevnik
macrumors 68020
 
Krevnik's Avatar
 
Join Date: Sep 2003
HTTP is a tricky beast because you aren't guaranteed what the content encoding will be. The headers should be UTF8 safe though.

For something like this, it would actually probably be better to use NSURLConnection instead which will handle the HTTP bits for you and make it easier to build a request and get the response.
__________________
iMac 2013 27", 13" rMBP, iPad 4, iPhone 5s
Krevnik is offline   0 Reply With Quote
Old Jun 9, 2009, 12:19 PM   #3
turner296
Thread Starter
macrumors newbie
 
Join Date: Jan 2009
Quote:
Originally Posted by Krevnik View Post
HTTP is a tricky beast because you aren't guaranteed what the content encoding will be. The headers should be UTF8 safe though.

For something like this, it would actually probably be better to use NSURLConnection instead which will handle the HTTP bits for you and make it easier to build a request and get the response.
Thanks for your reply so soon, I have all the headers as a NSString and have appended the request NSMutableData using the dataUsingEncoding:NSUTF8StringEncoding in the string object. Its really getting me down because why would it work with PERL but not COCOA. I really dont want to use NSURL I need to track the progress of the bits being sent to the stream. Unless that can be done with NSURL framework????
turner296 is offline   0 Reply With Quote
Old Jun 9, 2009, 12:26 PM   #4
Krevnik
macrumors 68020
 
Krevnik's Avatar
 
Join Date: Sep 2003
Quote:
Originally Posted by turner296 View Post
Thanks for your reply so soon, I have all the headers as a NSString and have appended the request NSMutableData using the dataUsingEncoding:NSUTF8StringEncoding in the string object. Its really getting me down because why would it work with PERL but not COCOA. I really dont want to use NSURL I need to track the progress of the bits being sent to the stream. Unless that can be done with NSURL framework????
Yes, NSURLConnection will provide data from the body as it comes in, and will expose the response headers/status as properties and a dictionary. The thing I see is that you assume the incoming data is ISO Latin. Assuming the strings coming in are the same, any difference would be due to assuming it is latin. Perl by default uses UTF8, I believe.

Using NSURLConnection should make it easier to find out what the content encoding is and use that when you take the body and convert it into a string.

It might also be helpful to see what string you get from perl from the server, and the string your Obj-C code thinks it is getting.
__________________
iMac 2013 27", 13" rMBP, iPad 4, iPhone 5s
Krevnik is offline   0 Reply With Quote
Old Jun 9, 2009, 01:30 PM   #5
turner296
Thread Starter
macrumors newbie
 
Join Date: Jan 2009
Quote:
Originally Posted by Krevnik View Post
Yes, NSURLConnection will provide data from the body as it comes in, and will expose the response headers/status as properties and a dictionary. The thing I see is that you assume the incoming data is ISO Latin. Assuming the strings coming in are the same, any difference would be due to assuming it is latin. Perl by default uses UTF8, I believe.

Using NSURLConnection should make it easier to find out what the content encoding is and use that when you take the body and convert it into a string.

It might also be helpful to see what string you get from perl from the server, and the string your Obj-C code thinks it is getting.
Thanks again for your help but I dont think is to do with the encoding to be honest, when I do the upload in PERL it sends me back the MD5 checksum of the uploaded file, when I do it in COCOA it sends me the NSData containing the servers homepage HTML. Its as if its not sending the data to the right host, or its sent it to the right host but a connection was lost so a redirect from the server has made the inputstream download the homepage. I dont know if you understand that?

if i was going to go with NSURL how would I track the progress of the application SENDING the data, NOT recieveing??
turner296 is offline   0 Reply With Quote
Old Jun 9, 2009, 01:43 PM   #6
Krevnik
macrumors 68020
 
Krevnik's Avatar
 
Join Date: Sep 2003
Quote:
Originally Posted by turner296 View Post
Thanks again for your help but I dont think is to do with the encoding to be honest, when I do the upload in PERL it sends me back the MD5 checksum of the uploaded file, when I do it in COCOA it sends me the NSData containing the servers homepage HTML. Its as if its not sending the data to the right host, or its sent it to the right host but a connection was lost so a redirect from the server has made the inputstream download the homepage. I dont know if you understand that?

if i was going to go with NSURL how would I track the progress of the application SENDING the data, NOT recieveing??
NSURL is a URL string utility class. NSURLConnection is a class that wraps up URL access & streams into an easy-to-use class. Huge difference.

NSURLConnection uses streams/notifications to send request data and receive response data. That isn't very different.

So now I wonder, what are you sending in each case? You say it is the same thing, but if it was, you should get the same response from the server. So something must be missing in the Obj-C version.
__________________
iMac 2013 27", 13" rMBP, iPad 4, iPhone 5s
Krevnik is offline   0 Reply With Quote
Old Jun 9, 2009, 01:56 PM   #7
Sayer
macrumors 6502a
 
Sayer's Avatar
 
Join Date: Jan 2002
Location: Austin, TX
Get HTTPScoop and run the Cocoa app a few times to see what is happening at the HTTP exchange level (actual HTTP connection, response, headers, etc).

It's an invaluable tool when you are doing web-based app development.

The only other alternative to get deep into the networking transactions is to use WireShark on a Windows box to view a 'tcpdump' file from a Mac. WireShark for Mac is a joke, if you can get on a Windows laptop or use BootCamp you can make a tcpdump log file using:

Terminal command:

Code:
sudo tcpdump -i en0 -vvv -n -s 0 -w -~/Desktop/DumpFile.dmp
Hit Control-C to stop logging. Move the .dmp file to Windows and open it with WireShark. That will show everything going on down to the TCP networking level.

I had problems talking to a certain DSL modem once using a spec called TR-064, which is just UPnP (SOAP/XML) with HTTP Digest authentication added on. The modem wasn't sending a MIME type header back of 'text-xml'. The UPnP's SOAP library was crapping out without even trying to parse the response as it was not in-spec without the MIME type header.

The modem maker fixed their firmware and sent me a test rev to burn and suddenly it all worked perfectly.

Without seeing the low-level responses I wouldn't have figured out it was such a simple thing that was wrong, and the modem that was at fault not my code. HTTPScoop is your friend.
__________________
Obama is a true statesman whose experience as a state senator, half-term US Senator & guest lecturer in a Constitutional Law class has fully prepared him to take control of our nuclear arsenal.-Me
Sayer is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
Frustrating Wi-Fi issues Darkaholic MacBook Pro 2 Aug 8, 2013 09:16 PM
Frustrating iPhone 4S Problem KnicksDude iPhone 6 Jun 23, 2013 06:24 AM
iPhone: MyLocation App is frustrating me. sassenach74 Jailbreaks and iOS Hacks 4 May 5, 2013 04:10 PM
So Frustrating... Why can't I buy any apps oo7ml iOS 6 3 Nov 12, 2012 06:42 AM
This PowerMAC G5 is so frustrating! Zeke D PowerPC Macs 52 Oct 12, 2012 04:50 AM

Forum Jump

All times are GMT -5. The time now is 06:50 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC