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?
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.
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.
@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.
make a method that returns a request and do
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:[self requestMethod] delegate:self];
?