ASIHTTPRequest Understanding persistent connection reuse
2012-02-13 16:19
351 查看
转自: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
问:
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
相关文章推荐
- ASIHTTPRequest "A connection failure occurred" error
- ASIHTTPRequest下载文件总报“Unable to start HTTP connection”错误,何解???
- ASIHTTPRequest "A connection failure occurred" error
- ASIHTTPRequest "A connection failure occurred" error
- 开源一个网络库ARHttpRequest,iOS适配NSURLSession/ASIHTTPRequest等,Android适配HttpURLConnection等
- ASIHTTPRequest详解 [经典3]
- 用ASIHttpRequest实现数据缓存和队列
- IOS-AFNetworking与ASIHTTPRequest的区别
- 使用ASIHTTPRequest 编译提示找不到"libxml/HTMLparser.h"的解决方法
- 第三方苹果开发库之ASIHTTPRequest(翻译版)
- ASIHttpRequest(HTTP Network库)
- ASIHttpRequest出错提示:Unable to create request (bad url?)
- ASIHTTPRequest 上传文件无响应问题研究
- ASIHttpRequest登录与Basic验证
- ASIHttpRequest请求HTTPS
- ASIHTTPRequest系列(四):Cookies
- 关于UrlHttpConnection.setRequestProperty()的调用顺序问题的验证
- 关于UrlHttpConnection.setRequestProperty()的调用顺序问题的验证
- ASIHTTPRequest类库简介和使用说明
- iOS常用开源框架之ASIHTTPRequest