ASIHTTPRequest Understanding persistent connection reuse

来源:互联网 发布:微信网页授权域名 编辑:程序博客网 时间:2024/06/06 11:50

转自:http://groups.google.com/group/asihttprequest/browse_thread/thread/49f58c66f00d993a/ce14ee901960e92a


问:

Hey all, 

Either I see a problem in the ASI stuff, or am not understanding how 
persistent connections are supposed to work in it.  Either way... 

I've integrated a031a030f949 into my codebase (was HEAD not too long 
ago if it isn't still) in order to add Keep-Alive support to my 
project.  I'm still in the "get it back to where it was before 
upgrading" stage, so I haven't done any work to get Keep-Alives 
working. 

I use the NSOperation method of doing requests.  I create a request 
(sometimes ASIHTTPRequest, sometimes ASIFormDataRequest) every time I 
want to do a new transaction. 

The behavior I'm seeing is that at the start of the app, the 
connections all work.  After that, none of them work. They all have an 
error as follows: 
2010-03-08 10:00:59.977 MyApp[47253:207] RESTHelper[0x54207f0]: Error 
was Error Domain=ASIHTTPRequestErrorDomain Code=1 UserInfo=0x5413a80 
"A connection failure occurred" 
2010-03-08 10:01:00.018 MyApp[47253:207] RESTHelper[0x54207f0]: Error 
userInfo is { 
    NSLocalizedDescription = "A connection failure occurred"; 
    NSUnderlyingError = Error Domain=NSPOSIXErrorDomain Code=57 
UserInfo=0x1293800 "Operation could not be completed. Socket is not 
connected"; 



So.. how *should* I be creating the connections?  Keeping a pool of my 
own to reuse?  That seems in keeping with the 
ASIHTTPRequest#connectionCanBeReused property.  I notice that each 
ASIHTTPRequest has its own connection pool, and I don't understand why 
that would be, since it's an instance variable, not global. 

Any help you can offer would be immensely appreciated.  :) 

Jimmy 


答:

Hi Jimmy 

> Either I see a problem in the ASI stuff, or am not understanding how 
> persistent connections are supposed to work in it.  Either way... 

As you've discovered, the way persistent connections work is rather confusing. :) This is mostly because the way they work in CFNetwork is very confusing (and, as far as I can see, entirely undocumented, except for a few Apple mailing list posts...) 

ASIHTTPRequest has a connection pool, stored in the static array persistentConnectionsPool. Each item in this pool is actually a dictionary that contains: 

* information about the host, port and scheme (eg http) the connection is for 
* a reference to the request that is currently using a connection (if there is one) 
* a reference to the stream used for the request 
* an ID that uniquely identifies the connection 
* a date corresponding with when the connection should expire 

When a request is started, it first looks in the connection pool to see if there is a connection it can reuse. It will attempt to reuse a connection if: 

* The host, port and scheme are the same as the request 
* The date the connection is supposed to expire has not yet passed 
* The connection is not already in use (if it has a value for the request key, it is already in use) 

If it finds a connection to use, the request will set its connectionInfo property to the dictionary for the connection it will use. If not, it creates a new connectionInfo dictionary (since it will need to create a new connection) and adds it to the pool. If it finds any connections that should have expired, it removes them from the pool. 

Behind the scenes, CFNetwork keeps connections open as long as there is a stream attached to that connection that hasn't been closed. So, when a request finishes, we actually keep the readStream open so a subsequent request can use the connection - once a new request starts and its stream has been opened, we close the old stream. 

Each connection's unique ID is used to 'tag' the readStream to tell CFNetwork 'reuse the connection that matches this id, or create a new connection if you can't find a connection that matches this id'. As far as I know, there is no way to access to the actual connection itself - all you can do is to provide CFNetwork with a hint as to your intention. 

- 显示引用的文字 -

Each request has its own connectionInfo dictionary, and the pool (which is global for all ASIHTTPRequests) is an array of these connectionInfo dictionaries. 

You shouldn't really need to do anything special to make persistent connections work. connectionCanBeReused is really only used internally - it has a public accessor so I can see what a request is doing in tests. The only property you should generally need to worry about is shouldAttemptPersistentConnection - if you set this to NO, ASIHTTPRequests won't attempt to use persistent connections at all. 

The first step in debugging problems with persistent connections should be to turn on DEBUG_PERSISTENT_CONNECTIONS (the easiest way is to modify ASIHTTPRequestConfig.h). This will cause ASIHTTPRequest to print information about how it is using and reusing connections to the console. 

Also, check the headers the server you are connecting to returns. If it returns a Keep-Alive header, ASIHTTPRequest can use the information about when the connection should expire to prevent the kind of problem you've been seeing (I'm guessing, it is trying to use connections that should have been closed). If it isn't returning a keep-alive header, ASIHTTPRequest will attempt to reuse the connection for 60 seconds after the request using it finishes. Depending on the server you're connecting to, this may be the wrong number entirely - servers tuned for performance may well close unused connections much earlier. 

I think the main flaw in the current implementation is ASIHTTPRequest won't attempt to retry if the server closes the connection - I hope to look at this fairly soon. 

I hope this makes sense! 

Best 

Ben


原创粉丝点击