Android通过HttpConnectionParams类为http参数设置提供了两个超时的设置选项,分别是setSoTimeout和setConnectionTimeout。初看一眼Android官方给的文档对这个两个方法的具体含义有一种不理解不够清楚的感觉,所以决定通过测试的方式来看看到底在什么情况下这两个参数会起作用。
经过测试可以发现,setConnectionTimeout设置了建立连接的超时,这是针对TCP的三次握手而言的,如果在指定时间内无法和http服务器建立TCP连接,就会抛出ConnectionTimeoutException。setSoTimeout则设置的是TCP保活时间,在建立了连接之后的指定时间内没有收到服务器发来的相应的数据包,则抛出SocketTimeoutException.
private String urlString; private int timeout1,timeout2; MyAsyncTask(String url,int timeout1,int timeout2){ urlString=url; this.timeout1=timeout1; this.timeout2=timeout2; } @Override protected String doInBackground(String... params) { httpget(urlString,timeout1,timeout2); return "test"; } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyAsyncTask("http://www.xiaonei.com:1234",3000,5000).execute("start"); new MyAsyncTask("http://www.baidu.com",3000,5000).execute("start"); } private void httpget(String Url,int timeout1,int timeout2) { Log.v("httpget", "httpget start timeout1 is "+timeout1+"timeout2 is "+timeout2); int timeoutConnection = timeout1; // until connection is established int timeoutSocket = timeout2; // timeout for waiting for data HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters); HttpGet postRequest = new HttpGet(Url); HttpResponse httpResp = null; try { httpResp = httpClient.execute(postRequest); } catch (ClientProtocolException e) { Log.v("Main", "clinet protocol exception"); return; }catch (SocketTimeoutException e) { // TODO: handle exception Log.v("Main", "socket timeout"+timeout2); return; }catch (ConnectTimeoutException e) { // TODO: handle exception Log.v("Main", "connection timeout"+timeout1); return; } catch (IOException e) { Log.v("Main", "io exception"); return; } }
在httpget的url中写入一个无法进行通信的地址如“10.1.1.1”或者是一个公开服务器没有开放的端口号如“www.xiaonei.com:1234”都会出现ConnnectionTimeoutException.对于一个无法通信的IP地址,Android系统直接构造一个发送至改地址的TCP建立连接数据包,不过由于该地址不存在,所以不会收到确认,到了设定好的超时时间就发生超时事件。对于不存在端口号的情况基本类似。
setSoTimeout设置了socket的保活超时时间。这里的测试方法是通过给Android终端设置代理,在代理收到了Android终端发来的http请求之后,直接截获http请求不予发送,到了超时时间抛出socketTimeoutException.这样一种http数据包截获的方式可以通过fiddler方便地实现。利用fiddler代理功能进行Androidhttp抓包配置方法见http://www.oschina.net/question/221817_129716,fiddler拦截数据包的方法见http://www.mzwu.com/article.asp?id=3509。
配置了fiddler代理并对http包进行拦截之后,运行测试程序可以得到如下输出。两个http请求都输出了socket timeout。第一个请求之所以也输出了socket timeout,是因为Android终端和代理之间能够正常建立连接,所以不会出现connection timeout,但是代理转交了Android终端发来的http请求之后无法获得响应,也就无法将相应的信息反馈给客户端,客户端出现了socket timeout。
从以上测试结果中可以看出,虽然在oncreate函数中同时调用了AsyncTask类分别发送两个不同的http请求,但是第二个被调用的Asyntask需要等到第一个被调用的超时之后才能被执行。这是因为Android在3.0之后,AsyncTask的实现上将所有的Asynctask都放到了一个线程中,按照一定的顺序有先后的执行。更详细的分析可以参考http://blog.csdn.net/singwhatiwanna/article/details/17596225。
[1]关于http超时参数的解释:http://stackoverflow.com/a/18185568/1767800
[2]关于如何得到http连接超时:http://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error