I've been using NSURLConnection to do a HTTP post to establish the connection. I've also implemented the didReceiveData delegate to process incoming bytes as they become available.
As incoming data comes in via didReceiveData, I add the NSData to a data buffer and try parsing the bytesteam if enough data has come in to complete a message segment. I'm having a hard time managing the data buffer (NSMutableData object) to remove bytes that have been parsed to structs. Was curious if there's an easier way. My didReceiveData delegate is below.
It works, but I don't think I'm managing memory correctly after I copy the message segment (currMsg) out of the responseData buffer and call processMsg. I get double free errors when running under the Simulator -- the program doesn't crash.
NSMutableData/NSData provide methods for appending bytes to the end but I didn't see any methods for removing bytes from the beginning (bytes representing whats already been parsed. I would appreciate some advice on how to best remove the parsed bytes from the responseData buffer. I come from a mostly C background so I'm not sure if there are better ways of manipulating the NSData bytes pointer. I'd like to avoid copying if possible -- just want to process a portion of the responseData buffer and leave the rest in responseData for next time enough bytes are in it for parsing.
Thanks
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSData *tmpBuffer = nil;
NSInteger currMsgSize = 10;
[responseData appendData:data];
NSInteger rspDataLen = [responseData length];
while(rspDataLen >= 10) {
currMsg = [[NSData alloc] initWithBytesNoCopy:(void *)[responseData bytes] length:currMsgSize];
[self processMsg:currMsg];
[currMsg release];
[responseData getBytes:tmpBuffer range:NSMakeRange(currMsgSize, rspDataLen - currMsgSize)];
[responseData release];
responseData = [[NSMutableData alloc] initWithBytesNoCopy:(void *)tmpBuffer length:rspDataLen - currMsgSize];
rspDataLen = rspDataLen - currMsgSize;
}
}
Where do you allocate the first responseData?
What is [self processMsg:currMsg] doing with the data? If it is expecting the data to be around after -processMsg: returns, and it isn't explicitly making a copy, then you are in trouble.
Infact, unless you have finished with the received data before didReceiveData: returns, you need to make a copy of it somewhere, which isn't visible in the code shown.
You need to allocate the storage for tempBuffer, not pass in an uninitialised pointer;
You should look probably for a pre-rolled implementation of a simple ring buffer. There are plenty around.
Related
I am trying to set up a NSURLRequest to download a simple index.html with its externa style.css sheet but I am not quite sure how to do this.. I have only ever just formatted the URL of the request to the file I want.. but this has to be slightly different and I cannot find a good example of what I am trying to do.
this is my code so far:
#pragma mark - NSURLConnection methods
- (void)htmlRequest
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.mywebsite.com/index.html"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
NSLog(#"Connection Fail");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// inform the developer of error type
}
// This method uses methodName to determin which Initalizer method to send the response data to in EngineResponses.m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// EngineResponses *engineResponses = [EngineResponses sharedManager];
// [engineResponses GetManufacturers:receivedData];
NSString *myString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", myString);
}
as you can see I am just calling index.html directly.. I would like to know how to format my request so i get the index.html as well as style.css
any help would be greatly appreciated.
I always create a new data structure,which has a -connection property and a -request property,like this
#interface connectionWrapper : NSObject
#property(retain) NSURLRequest *request
#property(retain) NSURLConnection *connection
by retaining this data structure in an mutable array, you can distinguish the connections in callback methods by iterate the array and compare each connectionWrapper instance's -connection property with the connection parameter the of the callback method, if they match(points to a same object), then you can retrieve the -request property of the connectionWrapper instance, then -url property of NSURLRequest instance.
as I'm not an native English speaker, I think code is a better tutor.
-(NSURLRequest*)getRequestByConnection:(NSURLConnection*)connection
{
for(connectionWrapper *w in theArrayContainingAllConnectionWrappers)
{
if(w == connection)
return w.request;
}
}
In callback method:
-(void)connection:(NSURLConnection*)connection didReceiveResponse(NSURLResponse*)response
{
NSURLRequest *request = [self getRequestByConnection:connection];
NSURL *url = [request url];
/*apply different approach to different url/*
}
PS:it's very sad that NSURLConnection don't have a -request property so that we can retrieve the request associated with the connection easily.
One way or another, you will have to make 2 requests. Even if you open a web page directly in a web browser, the browser will make a separate request for the CSS file referenced in the HTML it downloads. If your application needs both the HTML and the CSS file, then you want it to make 2 separate URL requests, first to get the HTML and then to get the CSS file.
Now, just because 2 requests need to be made, that doesn't mean you will always need to write the code that makes those 2 requests. It may be that libraries like the ones recommended by #Slee automatically take the results of a first request, parse them out, and make requests for any referenced CSS files. I have not worked with them so I am not sure what they support, or if any libraries will do this for you.
One thing you may want to consider is loading the HTML and CSS through a UIWebView rather than handling it all manually. UIWebView will attempt to load, parse, and render an HTML file into a UI component. In the process it will load referenced CSS and JavaScript files and apply them to its rendering. If you want to do anything special like intercept the calls it makes to load the CSS file(s), you can implement the UIWebViewDelegate protocol and set the delegate of the the UIWebView. Within that delegate you can implement the -webView:shouldStartLoadWithRequest:navigationType: method to be notified when the web view is loading the CSS file. You can use the call to that method to look at the request that is being issued for the CSS and do something else interesting with the request.
do you know the name of the .css file?
If so I would just make 2 requests otherwise you will have to write a parser to look for the link to the css and make a second request anyways.
I'd also suggest looking into a library to handle the downlading of stuff - lot's of great libraries that can do the heavy lifting for you with advanced features.
Here's 3 I have used:
http://blog.mugunthkumar.com/coding/ios-tutorial-advanced-networking-with-mknetworkkit/
https://github.com/tonymillion/TMHTTPRequest
https://github.com/pokeb/asi-http-request
I'm trying to download several images in response to a single http request. On the server side (java) I'm using oreilly multipart response and I'm getting my datas in my iPhone Simulator in didReceiveData (approximately one call for each image) after a call to didReceiveResponse (approximately one call for each image as well) in my delegate.
The problem is this approximately... Has anyone ever managed to handle correctly multipart/x-mixed-re with iPhone SDK ? If yes what is the best strategy here ? Should I play with the expected length ? on server side ? on client side ? should I wait until I've received everything... mmmh that doesn't even seen enough as the calls to didReceiveData happens in a random order (I'm asking picture1,picture2 and I'm sometimes receiving picture2,picture1 even though the order is respected on server side !). Should i temporize between pictures on server side ?
Or should I drop multipart/x-mixed-replace ? what would be the easiest then ?
That's a lot of questions but I'm really stuck here ! Thanks for you help !
I'm not sure what your final use for the images is, but the intended purpose of the multipart/x-midex-replace content type is for each received part to completely replace the previously received responses. Think of it like frames of a video; only one picture is displayed at a time and the previous ones are discarded.
Temporizing is almost never a foolproof solution. Especially on the iPhone you're going to encounter an unimaginable variety of network situations and relying on a magic number delay between frames will probably still fail some of the time.
Since you have control of the server, I'd recommend dropping the multipart. Make sure when you are sending multiple requests to the server that you don't block the main thread of your iPhone app. Use NSOperations or an alternative HTTP library (like ASIHTTPRequest) to make your image fetch operations asynchronous.
I did that successfully using this code. The important thing is to create 2 buffers to receive your data. If you use only one you will have some double access problems (stream access and jpg CODEC access) and corrupted JPG data.
Do not hesitate to ask me for more details.
- (IBAction)startDowload:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://210.236.173.198/axis-cgi/mjpg/video.cgi?resolution=320x240&fps=5"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
/*
I create 2 NSMutableData buffers. This points is very important.
I swap them each frame.
*/
receivedData = [[NSMutableData data] retain];
receivedData2 = [[NSMutableData data] retain];
currentData = receivedData;
urlCon = [[NSURLConnection alloc] initWithRequest:req delegate:self];
noImg = YES;
frame = 0;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is declared as a method instance elsewhere
UIImage *_i;
#try
{
_i = [UIImage imageWithData:currentData];
}
#catch (NSException * e)
{
NSLog(#"%#",[e description]);
}
#finally
{
}
CGSize _s = [_i size];
[imgView setImage:_i];
[imgView setNeedsDisplay];
[[self view] setNeedsDisplay];
}
/*
Buffers swap
*/
if (currentData == receivedData)
{
currentData = receivedData2;
}
else
{
currentData = receivedData;
}
[currendData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the currentData (NSData buffer)
[currendData appendData:data];
}
I am saving arrays of doubles in an NSData* object that is persisted as a binary property in a Core Data (SQLite) data model. I am doing this to store sampled data for graphing in an iPhone app. Sometimes when there are more than 300 doubles in the binary object not all the doubles are getting saved to disk. When I quit and relaunch my app there may be as few as 25 data points that have persisted or as many as 300.
Using NSSQLitePragmasOption with synchronous = FULL and this may be making a difference. It is hard to tell, as bug is intermittent.
Given the warnings about performance problems as a result of using synchronous = FULL, I am seeking advice and pointers.
Thanks.
[[Edit: here is code.]]
The (as yet unrealized) intent of -addToCache: is to add each new datum to the cache but only flush (fault?) Data object periodically.
From Data.m
#dynamic dataSet; // NSData * attribute of Data entity
- (void) addDatum:(double_t)datum
{
DLog(#"-[Data addDatum:%f]", datum);
[self addToCache:datum];
}
- (void) addToCache:(double_t)datum
{
if (cache == nil)
{
cache = [NSMutableData dataWithData:[self dataSet]];
[cache retain];
}
[cache appendBytes:&datum length:sizeof(double_t)];
DLog(#"-[Data addToCache:%f] ... [cache length] = %d; cache = %p", datum, [cache length], cache);
[self flushCache];
}
- (void) wrapup
{
DLog(#"-[Data wrapup]");
[self flushCache];
[cache release];
cache = nil;
DLog(#"[self isFault] = %#", [self isFault] ? #"YES" : #"NO"); // [self isFault] is always NO.
}
- (void) flushCache
{
DLog(#"flushing cache to store");
[self setDataSet:cache];
DLog(#"-[Data flushCache:] [[self dataSet] length] = %d", [[self dataSet] length]);
}
- (double*) bytes
{
return (double*)[[self dataSet] bytes];
}
- (NSInteger) count
{
return [[self dataSet] length]/sizeof(double);
}
- (void) dump
{
ALog(#"Dump Data");
NSInteger numDataPoints = [self count];
double *data = (double*)[self bytes];
ALog(#"numDataPoints = %d", numDataPoints);
for (int i = 0; i
I was trying to get behavior as if my Core Data entity could have an NSMutableData attribute. To do this my NSManagedObject (called Data) had an NSData attribute and an NSMutableData ivar. My app takes sample data from a sensor and appends each data point to the data set - this is why I needed this design.
On each new data point was appended to the NSMutableData and then the NSData attribute was set to the NSMutableData.
I suspect that because the NSData pointer wasn't changing (though its content was), that Core Data did not appreciate the amount of change. Calling -hasChanged on the NSManagedObjectContext showed that there had been changes, and calling -updatedObjects even listed the Data object as having changed. But the actual data that was being written seems to have been truncated (sometimes).
To work around this I changed things slightly. New data points are still appended to NSMutableData but NSData attribute is only set when sampling is completed. This means that there is a chance that a crash might result in truncated data - but for the most part this work around seems to have solved the problem.
Caveat emptor: the bug was always intermittent, so it is possible that is still there - but just harder to manifest.
I'm trying to code up an async image downloader. I use NSURLConnection to get the data into an NSMutableData and use that data once it is complete to initialize a UIImage.
I checked the bytes and it downloads the entire image correctly (right number of bytes at least), however; when I call
[UIImage imageWithData:data]
and then check the properties of the image, it is zero width and a garbage number for height, in fact, same number no matter what the image is. I tried with bunch of different images, png, jpg, different urls, it always downloads the image completely but UIImage can't initialize with that data. What could I be doing wrong here?
Thanks.
Code is really as you'd expect it to look like:
Connection Delegate:
-(void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
[[ImageManager sharedInstance] dataDownloadedBy:self]; }
ImageManager:
-(void)dataDownloadedBy:(WebConnection *)connection{
WebImage *image = [[WebImage alloc] initWithLink:connection.url];
[image setImageFromData:connection.data];
[images addObject:image];
[connection release];}
WebImage:
-(void)setImageFromData:(NSMutableData *)data{
image = [[UIImage alloc] initWithData:data];}
First, I'm sure the UIImage will not initialize with garbage data. The constructor initWithData analyzes the data to determine the file format. If your data is corrupted, the image returned will be nil. Check this at first.
-(void)dataDownloadedBy:(WebConnection *)connection{
WebImage *image = [[WebImage alloc] initWithLink:connection.url];
[image setImageFromData:connection.data];
if (image.image != nil) { [images addObject:image]; }
[connection release];
}
Second, make sure you append the data during the download process. Here is the callback method:
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
Finally, your code must absolutely includes the second case: a download failure.
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
}
Your problem description lacks some important pieces of code.
The URLCache demo from Apple is a very good project to understand async image download.
http://developer.apple.com/iphone/library/samplecode/URLCache/Introduction/Intro.html
I hope this will help you!
I can't tell what is wrong, it could be you don't use the release properly, say i don't know why you release connection at the end of dataDownloadedBy, it is supposed to be image ?
it could help if you post more your code here.
I used to do the same thing, you can have a look the post here
http://blog.163.com/lionyue#126/blog/static/1079307120096895433277/
Hope it helps
If initWithData is failing, most likely the image data you're getting is corrupt. You should save it to a file like this:
[data writeToFile:#"/tmp/foo.jpg" atomically:NO];
and then try to open it in Preview.app.
a different problem, but that gives teh same painful error is if You ask for a file that is not reachable: HTTP server will give back error 404 message, your code read that bytes (they are NOT valid bytes for an image, AND You will got NIL.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
xmlParseChunk(context, (const char *)[data bytes], [data length], 0);
}
my Question is as follows
=> didReceiveData: method receives data in parts
Some what like this
first data----------| <masterData>Main</ma
second data-----| ster><masterData>Main2
third data --------| </masterData>
=> How xmlParseChunk() method can parse all these chunks successfully?
Apple's XMLPerformance sample app illustrates a complete implementation of libxml2 integrated with NSURLConnection and chunk parsing. I found it very helpful.
One approach is to have your delegate contain an NSMutableData member and invoke appendData: when you get new data. Then parse it when your delegate gets the connectionDidFinishLoading message.