彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包

网络通信系列文章序

彻底掌握网络通信(一)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'
}

模拟器配置的wifi代理如下
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包_第1张图片

通过上面的代码,你会发现死活也抓不了包


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,第二个参数为你设置的端口号
通过上面的修改你就可以正常抓包了
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包_第2张图片


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代码去寻址;顾我们可以顺利的抓包了

你可能感兴趣的:(Android网络通信)