HttpClient https 请求TLS client hello 报文延时10秒发送

  1. 问题描述

报文抓包结果显示,TCP三次握手后(序号53),TLS延时10秒后才发送client hello报文(序号54)

  1. 问题分析

所有请求报文都产生了如下问题,初步分析问题原因是服务端发布的https服务,客户端使用的httpclient工具不兼容导致

  • TCP Out_of_Order的原因分析:

一般来说是网络拥塞,导致顺序包抵达时间不同,延时太长,或者包丢失,需要重新组合数据单元,因为他们可能是由不同的路径到达你的电脑上面。

  • TCP Retransmission原因分析:

很明显是上面的超时引发的数据重传。

  • TCP dup ack XXX#X原因分析:

就是重复应答#前的表示报文到哪个序号丢失,#后面的是表示第几次丢失。

  • tcp previous segment not captured原因分析

意思就是报文没有捕捉到,出现报文的丢失。

pom依赖
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.1.2</version>
</dependency>
客户端代码
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.KeyStore;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;

import com.ailk.aiip.enma.services.HttpClientService;
@Lazy(false)
public class HttpClientServiceImpl implements HttpClientService {

    final private Logger LOGGER = LoggerFactory.getLogger(getClass());

    private String keyStoreFilePath;
    private String keyStorePassword;
    private int serverPort;
    private String serverIp;
    private String url;
    private boolean sslEnable;
    private int connectionTimeout;
    private int soTimeout;
    private HttpClient httpClient;
    private int retryTimes;
    private String charset;

    private String keyStoreClientPath;
    private String keyStoreClientPassword;

    @PostConstruct
    public void init() {
        httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager());
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout * 1000);
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, soTimeout * 1000);
        httpClient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset);
        if (sslEnable) {
            try {
                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                FileInputStream instream = new FileInputStream(new File(keyStoreFilePath));
                keyStore.load(instream, keyStorePassword.toCharArray());

                KeyStore clientkeyStore  = KeyStore.getInstance("PKCS12");
                InputStream ksIn = new FileInputStream(keyStoreClientPath);
                clientkeyStore.load(ksIn, keyStoreClientPassword.toCharArray());

                SSLSocketFactory socketFactory = new SSLSocketFactory(clientkeyStore,keyStoreClientPassword,keyStore);
                socketFactory
                        .setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                Scheme sch = new Scheme("https", serverPort, socketFactory);
                httpClient.getConnectionManager().getSchemeRegistry().register(sch);
            } catch (Exception e) {
                LOGGER.error("init HttpClient error", e);
            }
        }
    }

    @PreDestroy
    public void destory() {
        if(httpClient!=null){
            httpClient.getConnectionManager().shutdown();
        }
    }


    @Override
    public String post(final String requestXml) {
        String responseXml = null;
        //post发送数据
        if(httpClient != null){
            try {
                String requestUrl = "http://";
                if (sslEnable) {
                    requestUrl = "https://";
                }
                requestUrl = requestUrl + serverIp + ":" + serverPort + url;
                for (int i = 0; i <= retryTimes; i++) {
                    HttpPost post = null;
                    try {
                        post = new HttpPost(requestUrl);
                        LOGGER.debug("post request to  {}", post.getURI());
                        ContentProducer contentProducer = new ContentProducer() {
                            @Override
                            public void writeTo(OutputStream out) throws IOException {
                                Writer writer = new OutputStreamWriter(out, charset);
                                writer.write(requestXml);
                                writer.flush();
                            }
                        };
                        post.setEntity(new EntityTemplate(contentProducer));
                        HttpResponse response = httpClient.execute(post);
                        responseXml = EntityUtils.toString(response.getEntity(), charset);
                        LOGGER.debug("post response is   {}", responseXml);
                        break;
                    } catch (Exception e) {
                        LOGGER.error("post error,retry count {}", i + 1,e);
                    }
                    finally{
                        if(post!=null){
                            post.abort();
                        }
                    }
                }
            } catch (Exception e) {
                LOGGER.error("post error", e);
            }
        }
        return responseXml;
    }


    public String getKeyStoreFilePath() {
        return keyStoreFilePath;
    }

    public void setKeyStoreFilePath(String keyStoreFilePath) {
        this.keyStoreFilePath = keyStoreFilePath;
    }

    public String getKeyStorePassword() {
        return keyStorePassword;
    }

    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    public int getServerPort() {
        return serverPort;
    }

    public void setServerPort(int serverPort) {
        this.serverPort = serverPort;
    }

    public String getServerIp() {
        return serverIp;
    }

    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isSslEnable() {
        return sslEnable;
    }

    public void setSslEnable(boolean sslEnable) {
        this.sslEnable = sslEnable;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public int getSoTimeout() {
        return soTimeout;
    }

    public void setSoTimeout(int soTimeout) {
        this.soTimeout = soTimeout;
    }

    public int getRetryTimes() {
        return retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public void setKeyStoreClientPath(String keyStoreClientPath) {
        this.keyStoreClientPath = keyStoreClientPath;
    }

    public void setKeyStoreClientPassword(String keyStoreClientPassword) {
        this.keyStoreClientPassword = keyStoreClientPassword;
    }
}
  1. 尝试解决方案

  • 修改过各项参数,如:100-continue,未成功

HttpProtocolParams.setUseExpectContinue(params, true);
  • 短连接修改为长连接,未成功

post.addHeader("Connection", "Keep-Alive");
  • 升级过httpclient版本,连接池由ThreadSafeClientConnManager改为PoolingClientConnectionManager,未成功

  • 引入commons-httpclient,替换org.apache.httpcomponents:httpclient,问题解决了

<dependency>
   <groupId>commons-httpclient</groupId>
   <artifactId>commons-httpclient</artifactId>
   <version>3.1</version>
</dependency>
public String post(final String requestXml) {
        String responseXml = null;
        try {
            String requestUrl = "http://";
            if (sslEnable) {
                requestUrl = "https://";
            }
            requestUrl = requestUrl + serverIp + ":" + serverPort + url;
            System.setProperty("javax.net.ssl.trustStore", getKeyStoreFilePath());
            System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
            HttpClient client = new HttpClient();
            PostMethod method = new PostMethod(requestUrl);
            method.setRequestBody(requestXml);
            LOGGER.debug("post requestXml is {}", requestXml);
            client.executeMethod(method);
            responseXml = method.getResponseBodyAsString();
            LOGGER.debug("post response is {}", responseXml);
            method.releaseConnection();
        } catch (Exception e) {
            LOGGER.error("post error", e);
        }
        return responseXml;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值