NSURLConnection is run many times
Asked Answered
C

4

7

I connect asynchronously with server each 5 seconds. The URL is the same, but POST-body is changed each time. Now I create NSURL, NSURLRequest and NSURLConnection from the scratch each time.

I think it'd be more effective to set connection once and just use that one further. I am a newbie and not sure if that possible. There is no mutable NSURLConnection, but may it's need to create NSURLConnection like:

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

and change NSMutableURLRequest POST-data to send another request to server. Which way is right?

Calliopsis answered 20/5, 2009 at 8:25 Comment(2)
if (!connection) connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [connection start]; This one is not working it gives “EXC_BAD_ACCESS”. I searched forums for that and there is no answer. It seems that 'start' is needed to use another way.Calliopsis
It's an oddity of this initializer method that you must schedule the connection on a runloop before starting it.Unreconstructed
Q
10

I assume what you're concerned about is the overhead of creating the HTTP connection. NSURLConnection is smart enough to handle this for you using HTTP/1.1 and reusing existing connections. It does not use pipelining last time I checked, but for your purpose, connection reuse should be sufficient. I do encourage you to put a network sniffer on this and make sure that it's working as you want them to.

The cost of creating the objects themselves is trivial on the order of once per 5s and you shouldn't try to optimize that (though of course you should reuse the NSURL). It's the opening a connection to the server that's expensive, especially on iPhone.

If you find you really do need pipelining, you unfortunately will have to roll your own. I've heard that CFHTTPStream can do it, but I don't see a lot of evidence of that. CocoaAsyncSocket is your best bet for low-level access to the sockets without having to write low-level code.

Since latency on the cell network can be very bad, it's possible that your connection will take longer than 5s to complete. Do make sure that one connection is done before starting the next, or you'll start making more and more open connections.

Quartus answered 20/5, 2009 at 14:4 Comment(4)
Big thanks. As I understand NSURL opens a connection, right? if so I do not need more optimizations, i just needed to not opening connection each 5 seconds, which is quite expensive, as you understand also.Calliopsis
NSURL itself does not open a connection. It just parses strings. NSURLConnection opens the connection, and it handles the HTTP/1.1 connection reuse for you.Quartus
I need to keep a connection alive and changing NSMutableURLRequest POST-data send different POSTs through it. Is it possible? How?Calliopsis
NSURLConnection does this transparently with HTTP/1.1's keep alive and BSD connection reuse, even when you use different NSURLConnection objects (as long as it's just one at a time). You don't need to do anything. Put wireshark on it, and you can see what's going on. Try implementing in the most obvious way and see how the performance is; then look at what needs optimizing, if anything.Quartus
S
6

Just to clarify things, NSURLConnection will reuse existing sockets, but only for a relatively small time frame (12 seconds). If you send a request, get back a response, and send a subsequent request within 12 seconds that 2nd request will got out on the same socket. Otherwise the socket will be closed by the client. A bug has been filed with Apple to increase this timer or to make it configurable.

Selinaselinda answered 30/4, 2012 at 17:12 Comment(0)
A
3

@Rob Napier @Eric Nelson As you mentioned: "NSURLConnection is smart enough to handle this for you using HTTP/1.1 and reusing existing connections". However, I can not find such description in any Apple's document about that.

To make thing clear, I write some code to test it:

- (IBAction)onClickSend:(id)sender {
    [self sendOneRequest];
}

-(void)sendOneRequest {

    NSURL *url = [NSURL URLWithString:@"http://192.168.1.100:1234"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:[Base64 encodeFromString:kValueVersion] forHTTPHeaderField:kKeyVersion];
    [request addValue:[Base64 encodeFromString:kValueDataTypeCmd] forHTTPHeaderField:kKeyDataType];
    [request addValue:[Base64 encodeFromString:@"Test"] forHTTPHeaderField:kKeyCmdName];
    [request addValue:[Base64 encodeFromString:@"Test"] forHTTPHeaderField:kKeyDeviceName];
    [request addValue:[Base64 encodeFromString:@"xxdafadfadfa"] forHTTPHeaderField:kKeyDTLCookies];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}

And then, I start wireshark to catch packages on the server(192.168.1.xxx), using "(tcp.flags.syn==1 ) || (tcp.flags == 0x0010 && tcp.seq==1 && tcp.ack==1)" to filter tcp 3-way hand shake. Unfortunately, I can see the 3-way hand shake for each calling of "sendOneRequest". Which means, the NSURLConnection seems not reuse the existing connections. Can some one point out what's wrong in my code and how to send multiple requests via one socket connection by NSURLConnection?

I also tried synchronous way to send request:

-(void)sendOneRequest {
    NSURL *url = [NSURL URLWithString:@"http://192.168.1.100:1234"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:[Base64 encodeFromString:kValueVersion] forHTTPHeaderField:kKeyVersion];
    [request addValue:[Base64 encodeFromString:kValueDataTypeCmd] forHTTPHeaderField:kKeyDataType];
    [request addValue:[Base64 encodeFromString:@"Test"] forHTTPHeaderField:kKeyCmdName];
    [request addValue:[Base64 encodeFromString:@"Test"] forHTTPHeaderField:kKeyDeviceName];
    [request addValue:[Base64 encodeFromString:@"xxdafadfadfa"] forHTTPHeaderField:kKeyDTLCookies];

    [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    sleep(1);

    [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    sleep(1);

    [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

}

And the result is the same.

=======UPDATE============================

Finally, I found the reason why my test is different from Rob and Eric say. In short, Rob and Eric are correct. And NSURLConnection uses “keep-alive” as default for using HTTP/1.1 and reuses existing socket connection, but only for a relatively small time frame.

However, NSURLConnection has some problems for “chunked transfer-coding”(ie. without content-length).

In my test, server side send a response without content-length and response data, and it's chunked response and NSURLConnection will close the connection, thus 3-way hand shake occurs for each http post.

I changed my server code, set the length of response as 0, and the behavior is correct.

Academicism answered 6/5, 2013 at 7:47 Comment(0)
R
0

make a method that returns a request and do

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:[self requestMethod] delegate:self];

?

Roaster answered 20/5, 2009 at 11:21 Comment(1)
[[NSURLConnection alloc] init...]; it creates a new connection each time, but I want use one connection for а long just changing a request's POST-data by: [request setHTTPBody: newRequestData];Calliopsis

© 2022 - 2024 — McMap. All rights reserved.