网络通信系列文章序
彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
当我们需要对一段请求分析的时候,通过会使用Fiddler来进行抓包,但是在你使用AsyncHttpClient之后,你会发现无法通过Fiddler来进行抓包
1:在通常的asynchttpclient开发中,一个post请求如下
private void sendPostNoEntity(){
AsyncHttpClient client = new AsyncHttpClient();
client.post("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes) {
Log.d("hwj", "**AsyncHttpClientActivity onSuccess**");
}
@Override
public void onFailure(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes, Throwable throwable) {
Log.d("hwj", "**AsyncHttpClientActivity onFailure**");
}
});
}
build.gradle文件内容如下
dependencies {
compile 'cz.msebera.android:httpclient:4.3.6'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
通过上面的代码,你会发现死活也抓不了包
2 : 解决方案:修改代码如下
private void sendPostNoEntity(){
AsyncHttpClient client = new AsyncHttpClient();
client.setProxy("172.29.14.249",8888);
client.post("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes) {
Log.d("hwj", "**AsyncHttpClientActivity onSuccess**");
}
@Override
public void onFailure(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes, Throwable throwable) {
Log.d("hwj", "**AsyncHttpClientActivity onFailure**");
}
});
}
我们在代码中添加了一行
client.setProxy("172.29.14.249",8888);
第一个参数为你模拟器设置的host,第二个参数为你设置的端口号
通过上面的修改你就可以正常抓包了
3:原因分析
3.1)我们在使用AsyncHttpClient的时候,供开发者使用的DefaultHttpClient继承AbstractHttpClient,无论DefaultHttpClient还是AbstractHttpClient,这两个类都在包cz.msebera.android.httpclient.impl.client下面
3.2)aysnchttpclient的一次http请求过程中,将一个http请求提交到线程池之后,AsyncHttpRequest.java的run方法在执行过程中会调用
HttpResponse response = client.execute(request, context);
来完成一次完整的http请求,而这里的client即为我们的DefaultHttpClient,我们看下client.execute方法到底是什么
public CloseableHttpResponse execute(
final HttpUriRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
return doExecute(determineTarget(request), request, context);
}
通过层层关系,可以知道doExecute方法的执行者为AbstractHttpClient.java
@Override
protected final CloseableHttpResponse doExecute(final HttpHost target, final HttpRequest request,
final HttpContext context)
throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
// a null target may be acceptable, this depends on the route planner
// a null context is acceptable, default context created below
HttpContext execContext = null;
RequestDirector director = null;
HttpRoutePlanner routePlanner = null;
ConnectionBackoffStrategy connectionBackoffStrategy = null;
BackoffManager backoffManager = null;
// Initialize the request execution context making copies of
// all shared objects that are potentially threading unsafe.
synchronized (this) {
final HttpContext defaultContext = createHttpContext();
if (context == null) {
execContext = defaultContext;
} else {
execContext = new DefaultedHttpContext(context, defaultContext);
}
final HttpParams params = determineParams(request);
final RequestConfig config = HttpClientParamConfig.getRequestConfig(params);
execContext.setAttribute(ClientContext.REQUEST_CONFIG, config);
// Create a director for this request
director = createClientRequestDirector(
getRequestExecutor(),
getConnectionManager(),
getConnectionReuseStrategy(),
getConnectionKeepAliveStrategy(),
getRoutePlanner(),
getProtocolProcessor(),
getHttpRequestRetryHandler(),
getRedirectStrategy(),
getTargetAuthenticationStrategy(),
getProxyAuthenticationStrategy(),
getUserTokenHandler(),
params);
routePlanner = getRoutePlanner();
connectionBackoffStrategy = getConnectionBackoffStrategy();
backoffManager = getBackoffManager();
}
try {
if (connectionBackoffStrategy != null && backoffManager != null) {
final HttpHost targetForRoute = (target != null) ? target
: (HttpHost) determineParams(request).getParameter(
ClientPNames.DEFAULT_HOST);
final HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext);
final CloseableHttpResponse out;
try {
out = CloseableHttpResponseProxy.newProxy(
director.execute(target, request, execContext));
} catch (final RuntimeException re) {
if (connectionBackoffStrategy.shouldBackoff(re)) {
backoffManager.backOff(route);
}
throw re;
} catch (final Exception e) {
if (connectionBackoffStrategy.shouldBackoff(e)) {
backoffManager.backOff(route);
}
if (e instanceof HttpException) {
throw (HttpException)e;
}
if (e instanceof IOException) {
throw (IOException)e;
}
throw new UndeclaredThrowableException(e);
}
if (connectionBackoffStrategy.shouldBackoff(out)) {
backoffManager.backOff(route);
} else {
backoffManager.probe(route);
}
return out;
} else {
return CloseableHttpResponseProxy.newProxy(
director.execute(target, request, execContext));
}
} catch(final HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
重点看下第26~28行,这三行代码主要配置了当前http请求上下文中的一些参数
(其他代码类似于Apache的HttpClient,这里就不另做分析)
那这些参数有哪些?我们看下27行
public static RequestConfig getRequestConfig(final HttpParams params) {
return RequestConfig.custom()
.setSocketTimeout(params.getIntParameter(
CoreConnectionPNames.SO_TIMEOUT, 0))
.setStaleConnectionCheckEnabled(params.getBooleanParameter(
CoreConnectionPNames.STALE_CONNECTION_CHECK, true))
.setConnectTimeout(params.getIntParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 0))
.setExpectContinueEnabled(params.getBooleanParameter(
CoreProtocolPNames.USE_EXPECT_CONTINUE, false))
.setProxy((HttpHost) params.getParameter(
ConnRoutePNames.DEFAULT_PROXY))
.setLocalAddress((InetAddress) params.getParameter(
ConnRoutePNames.LOCAL_ADDRESS))
.setProxyPreferredAuthSchemes((Collection) params.getParameter(
AuthPNames.PROXY_AUTH_PREF))
.setTargetPreferredAuthSchemes((Collection) params.getParameter(
AuthPNames.TARGET_AUTH_PREF))
.setAuthenticationEnabled(params.getBooleanParameter(
ClientPNames.HANDLE_AUTHENTICATION, true))
.setCircularRedirectsAllowed(params.getBooleanParameter(
ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false))
.setConnectionRequestTimeout((int) params.getLongParameter(
ClientPNames.CONN_MANAGER_TIMEOUT, 0))
.setCookieSpec((String) params.getParameter(
ClientPNames.COOKIE_POLICY))
.setMaxRedirects(params.getIntParameter(
ClientPNames.MAX_REDIRECTS, 50))
.setRedirectsEnabled(params.getBooleanParameter(
ClientPNames.HANDLE_REDIRECTS, true))
.setRelativeRedirectsAllowed(!params.getBooleanParameter(
ClientPNames.REJECT_RELATIVE_REDIRECT, false))
.build();
}
3.2.1)String SO_TIMEOUT = “http.socket.timeout”,表示服务器响应超时,默认时间为0,单位毫秒
3.2.2)String STALE_CONNECTION_CHECK = “http.connection.stalecheck”,表示是否对过期连接进行检查,关闭这个属性,一般可以提高30毫秒的性能
3.2.3)String CONNECTION_TIMEOUT = “http.connection.timeout”,表示建立连接超时,默认时间为0,单位毫秒
3.2.4)String USE_EXPECT_CONTINUE = “http.protocol.expect-continue”,表示是否激活Expect:100-Continue,默认值为false;备注:(Expect:100-Continue握手的目的,是为了允许客户端在发送请求内容之前,判断源服务器是否愿意接受
请求(基于请求头部)。
Expect:100-Continue握手需谨慎使用,因为遇到不支持HTTP/1.1协议的服务器或者代理时会引起问题。)
3.2.5)String DEFAULT_PROXY = “http.route.default-proxy”表示定义可以被不使用JRE设置的默认路由规划者使用的代理主机。这个参数期望得到一个HttpHost类型的值。如果这个参数没有被设置,那么就会尝试直接连接到目标,这个参数会被HttpRoutePlanner使用,HttpRoutePlanner是一个代表计算到基于执行上下文到给定目标完整路由策略的接口。HttpClient附带两个默认的HttpRoutePlanner实现。ProxySelectorRoutePlanner是基于java.net.ProxySelector的。默认情况下,它会从系统属性中或从运行应用程序的浏览器中选取JVM的代理设置。DefaultHttpRoutePlanner实现既不使用任何Java系统属性,也不使用系统或浏览器的代理设置。
3.2.6)String CONN_MANAGER_TIMEOUT = “http.conn-manager.timeout”表示从ClientConnectionManager中取出一个连接的时间,默认是为0,单位毫毛,在Apache的HttpClient中, ClientConnectionManager的默认实现为SingleClientConnManager,在SingleClientConnManager中,可以获取到ClientConnectionOperator,通过ClientConnectionOperator可以获取到http连接
我们重点看下第12行的参数设置,ConnRoutePNames.DEFAULT_PROXY,默认情况下,他是不使用代理进行路由寻址的,当我们通过
setProxy("172.29.14.249",8888);
方法,即改变了路由寻址的策略,将wifi直连改为通过wifi代码去寻址;顾我们可以顺利的抓包了