解决java Socket慢的问题

最近本人在业余时间想使用socket写一个访问网页的工具类,用来了解http。

在编写过程中发现,一个大概是5k的图片,用代码去下载时候用了足足30秒,但是在浏览器中访问同一个链接的时候只用了3ms,这个中间的差距我实在不能够接收。我是做了什么,我编写的代码这么慢。

经过我在谷歌上搜索发现read()方法当读取完数据之后就开始阻塞,等待返回-1结束,这个等待的过程占到了总时间的99%。可是如果不等待怎么知道结束了?数据不全的话,会造成解析出错。

通过查看返回的http头发现,在http返回头中Content-length对应的数字就是消息体的长度,只要通过判断获取的长度和头中的长度是一致的,就可以认为数据已经获取完毕了,没有必要等待返回-1来判断是否结束。

通过以上的修改,下载图片的速度终于上来了,达到了一秒以内。总算不那么慢了,之前那真是要疯了。

附上代码,仅供参考

package com.shxzhlxrr.util.socket;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.log4j.Logger;

import com.shxzhlxrr.util.StringUtil;
/**
 * 
 * @author zxr
 * @date 2017年2月6日 下午6:33:36
 */
public class HttpClient {
    /**
     * 主机
     */
    private String host;

    private int port = 80;

    private String url;// 主机后面的相对的路径

    @SuppressWarnings("unused")
    private String baseUrl;// 访问的完整的路径

    private String context;

    private String protocol;
    /**
     * 客户端的信息
     */
    private String userAgent;

    private InputStream contentInputStream;

    private byte[] contentData;

    private static Logger log = Logger.getLogger(HttpClient.class);

    private int status = 200;

    private Map<String, Object> header = new HashMap<String, Object>();

    public Map<String, Object> getHeader() {
        return header;
    }

    public HttpClient() {

    }

    /**
     * 推荐使用四个参数的构造方法,本构造方法对于url的解析存在一些问题<br/>
     * 可能会给你带来一些不必要的问题
     * 
     * @param url
     */
    public HttpClient(String url) {
        this.baseUrl = url;
        this.parseHttpUrl(url);
    }

    public HttpClient(String host, int port, String url, String protocol) {
        this.host = host;
        this.port = port;
        this.url = url;
        this.protocol = protocol;
        this.baseUrl = this.protocol + "://" + this.host + ":" + this.port + "/" + this.url;
    }

    /**
     * 获取返回的内容
     * 
     * @return
     * @throws Exception
     */
    public String getContext() {

        return context;
    }

    /**
     * 发送请求
     */
    public void send() throws Exception {
        if (this.protocol == null) {
            this.protocol = "http";
        }
        if (this.url == null) {
            this.url = "/";
        }

        validateParam();

        getHttpContent();
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUrl() {
        return url;
    }

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

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public InputStream getContentInputStream() {
        return contentInputStream;
    }

    public byte[] getContentData() {
        return contentData;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    private void getHttpContent() throws Exception {
        BufferedReader bf = null;
        PrintWriter pw = null;
        Socket soc = null;
        GZIPInputStream gzin = null;
        try {
            soc = getSocket();

            if (soc.isConnected()) {

                pw = new PrintWriter(soc.getOutputStream(), true);// 获取输出流

                sendReqMsg(pw);// 发送请求

                parseRes(soc);// 解析返回的请求数据

            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (bf != null) {
                try {
                    bf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
            if (soc != null) {
                try {
                    soc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (gzin != null) {
                gzin.close();
            }
        }
    }

    /**
     * 获取访问的socket
     * 
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws UnknownHostException
     * @throws KeyManagementException
     */
    private Socket getSocket()
            throws KeyManagementException, UnknownHostException, NoSuchAlgorithmException, IOException {
        Socket soc = null;
        if ("https".equals(this.protocol)) {
            soc = getHttpsSocket();
        } else {
            soc = new Socket(InetAddress.getByName(this.host), this.port);
        }
        return soc;
    }

    /**
     * 获取httpssocket
     * 
     * @return
     * @throws UnknownHostException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private Socket getHttpsSocket()
            throws UnknownHostException, IOException, NoSuchAlgorithmException, KeyManagementException {

        X509TrustManager xtm;
        try {
            xtm = new Java2000TrustManager();

            TrustManager mytm[] = { xtm };
            // 得到上下文
            SSLContext ctx = SSLContext.getInstance("SSL", "SunJSSE");
            // SSLContext ctx = SSLContext.getInstance("TLS");
            // 初始化
            ctx.init(null, mytm, new java.security.SecureRandom());
            // ctx.init(null, null, null);
            // 获得工厂
            SSLSocketFactory factory = ctx.getSocketFactory();

            // 从工厂获得Socket连接
            Socket socket = factory.createSocket(host, port);

            return socket;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析返回的数据
     */
    private void parseRes(Socket soc) throws IOException {
        long start = System.currentTimeMillis();
        InputStream in = soc.getInputStream();
        byte[] buf = new byte[1024 * 200];
        int isClose = 0;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        List<String> titles = new ArrayList<String>();
        while ((isClose = in.read()) != -1) {
            baos.write(isClose);
            if ('\n' == isClose) {
                String str = new String(baos.toByteArray());
                titles.add(str);
                baos.reset();
                if (StringUtil.isNull(str)) {// 这个时候表示到了空行。
                    parseHeader(titles);
                    break;
                }
            }
        }
        long end = System.currentTimeMillis();

        int count = 0;
        log.debug("[解析图片的头]:" + ((end - start) / 1000));
        log.debug("[返回的消息的头]:" + this.header);
        if ("chunked".equals(this.header.get("Transfer-Encoding"))) {
            baos = parseChunk(in);
        } else {
            int content_length = Integer.valueOf(String.valueOf(this.header.get("Content-Length")));
            int countNum = 0;
            while ((count = in.read(buf)) != -1) {
                baos.write(buf, 0, count);
                countNum = countNum + count;
                end = System.currentTimeMillis();
                log.debug("[解析内容]:" + ((end - start) / 1000));
                if (content_length == countNum) {
                    break;
                }
            }
        }

        end = System.currentTimeMillis();
        log.debug("[解析消息的主体]:" + ((end - start) / 1000));
        byte[] contentBuf = baos.toByteArray();
        if ("gzip".equals(this.header.get("Content-Encoding"))) {// 当是gzip压缩的时候,进行解压
            contentBuf = ungzip(contentBuf);
        }
        this.context = new String(contentBuf);
        this.contentData = contentBuf;
        this.contentInputStream = new ByteArrayInputStream(this.contentData);
        // }
        end = System.currentTimeMillis();
        log.debug("[解析完毕]:" + ((end - start) / 1000));
    }

    /**
     * 解析chunk编码的数据
     * 
     * @param in
     * @return
     */
    @SuppressWarnings("resource")
    private ByteArrayOutputStream parseChunk(InputStream in) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            out = parseChunk(out, in);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return out;
    }

    /**
     * 解析chunk编码的数据
     * 
     * @param out
     * @param in
     * @return
     * @throws IOException
     */
    public ByteArrayOutputStream parseChunk(ByteArrayOutputStream out, InputStream in) throws IOException {
        ByteArrayOutputStream temp = new ByteArrayOutputStream();
        int ch = 0;
        while ((ch = in.read()) != -1) {
            if ('\r' == ch) {
                continue;
            }
            if ('\n' == ch) {
                break;
            }
            temp.write(ch);
        }
        String tempStr = new String(temp.toByteArray());
        if (!"".equals(tempStr.trim())) {
            int count = Integer.parseInt(new String(temp.toByteArray()).trim(), 16);
            if (count == 0) {
                return out;
            }
            int num = 0;
            while (num < count) {
                int chr = in.read();
                out.write(chr);
                num++;
            }
        }
        out = parseChunk(out, in);

        return out;
    }

    /**
     * 校验基本的数据不能为空
     */
    private void validateParam() {
        assertNotNull("端口号不能为空", this.port);
        assertNotNull("主机不能为空", this.host);
        assertNotNull("访问的链接不能为空", this.url);
        assertNotNull("访问的协议不能为空", this.protocol);
        assertTrue("请设置协议为'http'或'https'", Pattern.compile("http[s]*").matcher(this.protocol).find());
    }

    /**
     * 发送请求信息
     */
    private void sendReqMsg(PrintWriter pw) {
        pw.println("GET " + this.url + " HTTP/1.1");
        pw.println("Accept:text/html,image/webp,*/*;q=0.8");
        pw.println("Accept-Encoding:gzip, deflate, sdch, br");
        pw.println("Accept-Language:zh-CN,zh;q=0.8");
        pw.println("Connection:keep-alive");
        if ("http".equals(this.protocol)) {
            pw.println("Host:" + this.host + ":" + this.port);
        } else {
            pw.println("Host:" + this.host);
        }
        if (this.userAgent != null) {
            pw.println(this.userAgent);
        } else {
            this.userAgent = "User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36";
            pw.println(this.userAgent);
        }
        pw.println("\r\n");
    }

    /**
     * 用来解析返回的数据的第一行
     * 
     * @param header
     */
    private void parseHeader(List<String> titles) {
        String header = titles.get(0);
        String[] haaders = header.split(" ");
        this.header.put("protocol", haaders[0].trim());
        this.header.put("status", haaders[1].trim());
        this.status = Integer.valueOf(haaders[1].trim());
        titles.remove(0);
        for (String title : titles) {
            if (StringUtil.isNull(title)) {
                continue;
            }
            String key = title.substring(0, title.indexOf(":"));
            String value = title.substring(title.indexOf(":") + 1);
            this.header.put(key.trim(), value.trim());
        }
        log.debug(this.header);
    }

    /**
     * 从一个url链接中解析出 host,端口,url,protocol 等
     * 
     * @param url
     */
    private void parseHttpUrl(String url) {
        // TODO 校验url是否是正确的
        String protocol = "http";
        if (url.startsWith("https")) {
            protocol = "https";
        }

        url = url.substring(url.indexOf("//") + 2);
        String host = url;
        if (url.indexOf("/") < 0) {
            url = "/";
        } else {
            host = url.substring(0, url.indexOf("/"));
            url = url.substring(url.indexOf("/"));
        }

        int port = 80;
        if ("https".equals(protocol)) {
            port = 443;
        }
        if (host.contains(":")) {
            port = Integer.valueOf(host.substring(host.indexOf(":") + 1));
            host = host.substring(0, host.indexOf(":"));
        }

        this.host = host;
        this.port = port;
        this.url = url;
        this.protocol = protocol;
    }

    /**
     * 将gzip压缩的字节数据进行解压
     * 
     * @param gzipbuf
     * @return
     * @throws IOException
     */
    public byte[] ungzip(byte[] gzipbuf) throws IOException {
        GZIPInputStream gzin = new GZIPInputStream(new ByteArrayInputStream(gzipbuf));
        BufferedInputStream bufis = new BufferedInputStream(gzin);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int count = 0;
        while ((count = bufis.read(buf)) != -1) {
            baos.write(buf, 0, count);
        }
        return baos.toByteArray();
    }

}

class Java200TrustManager implements X509TrustManager {
    X509TrustManager sunJSSEX509TrustManager;

    Java200TrustManager() throws Exception {
        // 这里可以进行证书的初始化操作,使用的是jdk自带的证书
        String cerPath = "cer/cacerts";
        InputStream in = getClass().getClassLoader().getResourceAsStream(cerPath);
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(in, "changeit".toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
        tmf.init(ks);
        TrustManager tms[] = tmf.getTrustManagers();
        for (int i = 0; i < tms.length; i++) {
            if (tms[i] instanceof X509TrustManager) {
                sunJSSEX509TrustManager = (X509TrustManager) tms[i];
                return;
            }
        }
        throw new Exception("Couldn't initialize");
    }

    // 检查客户端的可信任状态
    public void checkClientTrusted(X509Certificate chain[], String authType) throws CertificateException {
        // System.out.println("检查客户端的可信任状态...");
        // System.out.println("checkClientTrusted-------------------");
        // System.out.println("chain:" + chain);
        // System.out.println("authType:" + authType);

        sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
    }

    // 检查服务器的可信任状态
    public void checkServerTrusted(X509Certificate chain[], String authType) throws CertificateException {
        // System.out.println("检查服务器的可信任状态");
        sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
        // System.out.println("checkServerTrusted==========");
        // System.out.println("chain:" + chain);
        // System.out.println("authType:" + authType);
    }

    // 返回接受的发行商数组
    public X509Certificate[] getAcceptedIssuers() {
        // System.out.println("获取接受的发行商数组...");
        return sunJSSEX509TrustManager.getAcceptedIssuers();
        // return null;
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值