吃透HTTP一:Http请求HttpUrlConnection,HttpClientOKHttp详细介绍

Posted 吃透Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了吃透HTTP一:Http请求HttpUrlConnection,HttpClientOKHttp详细介绍相关的知识,希望对你有一定的参考价值。

一,HTTP概述

HTTP协议是无状态的协议,即每一次请求都是互相独立的。因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接。

HTTP协议是全双工的协议,所以建立连接与断开连接是要经过三次握手与四次挥手的。显然在这种设计中,每次发送Http请求都会消耗很多的额外资源,即连接的建立与销毁。

于是,HTTP协议的也进行了发展,通过持久连接的方法来进行socket连接复用。

HTTP/1.0+的Keep-Alive

使用HTTP/1.0的客户端在首部中加上"Connection:Keep-Alive",请求服务端将一条连接保持在打开状态。服务端如果愿意将这条连接保持在打开状态,就会在响应中包含同样的首部。如果响应中没有包含"Connection:Keep-Alive"首部,则客户端会认为服务端不支持keep-alive,会在发送完响应报文之后关闭掉当前连接。

通过keep-alive补充协议,客户端与服务器之间完成了持久连接,然而仍然存在着一些问题:

  • 在HTTP/1.0中keep-alive不是标准协议,客户端必须发送Connection:Keep-Alive来激活keep-alive连接。
  • 代理服务器可能无法支持keep-alive,因为一些代理是"盲中继",无法理解首部的含义,只是将首部逐跳转发。所以可能造成客户端与服务端都保持了连接,但是代理不接受该连接上的数据。
HTTP/1.1的持久连接

HTTP/1.1采取持久连接的方式替代了Keep-Alive。

HTTP/1.1的连接默认情况下都是持久连接。如果要显式关闭,需要在报文中加上Connection:Close首部。即在HTTP/1.1中,所有的连接都进行了复用。

然而如同Keep-Alive一样,空闲的持久连接也可以随时被客户端与服务端关闭。不发送Connection:Close不意味着服务器承诺连接永远保持打开。

二,HttpURLConnection简介

在JDK的java.net包中已经提供了访问HTTP协议的基本功能的类:HttpURLConnection。

HttpURLConnection是Java的标准类,它继承自URLConnection,可用于向指定网站发送GET请求、POST请求。它在URLConnection的基础上提供了如下便捷的方法:

int getResponseCode(); // 获取服务器的响应代码。
String getResponseMessage(); // 获取服务器的响应消息。
String getResponseMethod(); // 获取发送请求的方法。
void setRequestMethod(String method); // 设置发送请求的方法。
  • HttpURLConnection对象不能直接构造,需要通过URL类中的openConnection()方法来获得。
  • HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的TCP连接,并没有实际发送HTTP请求。HTTP请求实际上直到我们获取服务器响应数据(如调用getInputStream()、getResponseCode()等方法)时才正式发送出去。
  • 对HttpURLConnection对象的配置都需要在connect()方法执行之前完成。
  • HttpURLConnection是基于HTTP协议的,其底层通过socket通信实现。如果不设置超时(timeout),在网络异常的情况下,可能会导致程序僵死而不继续往下执行。
  • HTTP正文的内容是通过OutputStream流写入的, 向流中写入的数据不会立即发送到网络,而是存在于内存缓冲区中,待流关闭时,根据写入的内容生成HTTP正文。
  • 调用getInputStream()方法时,返回一个输入流,用于从中读取服务器对于HTTP请求的返回信息。
  • 我们可以使用HttpURLConnection.connect()方法手动的发送一个HTTP请求,但是如果要获取HTTP响应的时候,请求就会自动的发起,比如我们使用HttpURLConnection.getInputStream()方法的时候,所以完全没有必要调用connect()方法。
HttpURLConnection工具类封装:
package com.example.demohttp.httpurlconnection;

import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @Date: 2021/12/1 4:39 下午
 */
public class HttpUrlConnectionUtils 
    private static int DEFAULT_READTIME = 30000;
    private static int DEFAULT_CONNECTTIME = 30000;

    private static String READTIME_KEY = "readTime";
    private static String CONNECTTIME_KEY = "connectTime";

    /**
     * Send a get request
     * @param url
     * @return response
     * @throws IOException
     */
    static public String get(String url) throws IOException 
        return get(url, null);
    

    /**
     * Send a get request
     * @param url         Url as string
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String get(String url,
                             Map<String, String> headers) throws IOException 
        return fetch("GET", url, null, headers);
    

    /**
     * Send a post request
     * @param url         Url as string
     * @param body        Request body as string
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String post(String url, String body,
                              Map<String, String> headers) throws IOException 
        return fetch("POST", url, body, headers);
    

    /**
     * Send a post request
     * @param url         Url as string
     * @param body        Request body as string
     * @return response   Response as string
     * @throws IOException
     */
    static public String post(String url, String body) throws IOException 
        return post(url, body, null);
    

    /**
     * Post a form with parameters
     * @param url         Url as string
     * @param params      map with parameters/values
     * @return response   Response as string
     * @throws IOException
     */
    static public String postForm(String url, Map<String, String> params)
            throws IOException 
        return postForm(url, params, null);
    

    /**
     * Post a form with parameters
     * @param url         Url as string
     * @param params      Map with parameters/values
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String postForm(String url, Map<String, String> params,
                                  Map<String, String> headers) throws IOException 
        // set content type
        if (headers == null) 
            headers = new HashMap<String, String>();
        

        if (StringUtils.isBlank(headers.get("Content-Type")))
            headers.put("Content-Type", "application/x-www-form-urlencoded");
        

        // parse parameters
        String body = "";
        if (params != null) 
            boolean first = true;
            for (String param : params.keySet()) 
                if (first) 
                    first = false;
                 else 
                    body += "&";
                
                String value = params.get(param);
                body += URLEncoder.encode(param, "UTF-8") + "=";
                body += URLEncoder.encode(value, "UTF-8");
            
        

        return post(url, body, headers);
    

    /**
     * Send a put request
     * @param url         Url as string
     * @param body        Request body as string
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String put(String url, String body,
                             Map<String, String> headers) throws IOException 
        return fetch("PUT", url, body, headers);
    

    /**
     * Send a put request
     * @param url         Url as string
     * @return response   Response as string
     * @throws IOException
     */
    static public String put(String url, String body) throws IOException 
        return put(url, body, null);
    

    /**
     * Send a delete request
     * @param url         Url as string
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String delete(String url,
                                Map<String, String> headers) throws IOException 
        return fetch("DELETE", url, null, headers);
    

    /**
     * Send a delete request
     * @param url         Url as string
     * @return response   Response as string
     * @throws IOException
     */
    static public String delete(String url) throws IOException 
        return delete(url, null);
    

    /**
     * Append query parameters to given url
     * @param url         Url as string
     * @param params      Map with query parameters
     * @return url        Url with query parameters appended
     * @throws IOException
     */
    static public String appendQueryParams(String url,
                                           Map<String, String> params) throws IOException 
        String fullUrl = url;
        if (params != null) 
            boolean first = (fullUrl.indexOf('?') == -1);
            for (String param : params.keySet()) 
                if (first) 
                    fullUrl += '?';
                    first = false;
                 else 
                    fullUrl += '&';
                
                String value = params.get(param);
                fullUrl += URLEncoder.encode(param, "UTF-8") + '=';
                fullUrl += URLEncoder.encode(value, "UTF-8");
            
        

        return fullUrl;
    

    /**
     * Retrieve the query parameters from given url
     * @param url         Url containing query parameters
     * @return params     Map with query parameters
     * @throws IOException
     */
    static public Map<String, String> getQueryParams(String url)
            throws IOException 
        Map<String, String> params = new HashMap<String, String>();

        int start = url.indexOf('?');
        while (start != -1) 
            // read parameter name
            int equals = url.indexOf('=', start);
            String param = "";
            if (equals != -1) 
                param = url.substring(start + 1, equals);
             else 
                param = url.substring(start + 1);
            

            // read parameter value
            String value = "";
            if (equals != -1) 
                start = url.indexOf('&', equals);
                if (start != -1) 
                    value = url.substring(equals + 1, start);
                 else 
                    value = url.substring(equals + 1);
                
            

            params.put(URLDecoder.decode(param, "UTF-8"),
                    URLDecoder.decode(value, "UTF-8"));
        

        return params;
    

    /**
     * Returns the url without query parameters
     * @param url         Url containing query parameters
     * @return url        Url without query parameters
     * @throws IOException
     */
    static public String removeQueryParams(String url)
            throws IOException 
        int q = url.indexOf('?');
        if (q != -1) 
            return url.substring(0, q);
         else 
            return url;
        
    

    /**
     * Send a request
     * @param method      HTTP method, for example "GET" or "POST"
     * @param url         Url as string
     * @param body        Request body as string
     * @param headers     Optional map with headers
     * @return response   Response as string
     * @throws IOException
     */
    static public String fetch(String method, String url, String body,
                               Map<String, String> headers) throws IOException 
        // connection
        URL u = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
        conn.setConnectTimeout(DEFAULT_CONNECTTIME);
        conn.setReadTimeout(DEFAULT_READTIME);

        // method
        if (method != null) 
            conn.setRequestMethod(method);
        

        // headers
        if (headers != null) 
            for (String key : headers.keySet()) 
                if (READTIME_KEY.equals(key)) 
                    conn.setReadTimeout(Integer.parseInt(headers.get(key)));
                    continue;
                
                if (CONNECTTIME_KEY.equals(key)) 
                    conn.setConnectTimeout(Integer.parseInt(headers.get(key)));
                    continue;
                
                conn.addRequestProperty(key, headers.get(key));
            
        

        // body
        if (body != null) 
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            os.write(body.getBytes());
            os.flush();
            os.close();
        

        // response
        InputStream is = null;
        if (conn.getResponseCode() >= 400) 
            is = conn.getErrorStream();
         else 
            is = conn.getInputStream();
        
        String response = streamToString(is);
        is.close();

        // handle redirects
        if (conn.getResponseCode() == 301) 
            String location = conn.getHeaderField("Location");
            return fetch(method, location, body, headers);
        

        return response;
    

    /**
     * Read an input stream into a string
     * @param in
     * @return
     * @throws IOException
     */
    static public String streamToString(InputStream in) throws IOException 
        StringBuffer out = new StringBuffer();
        byte[] b = new byte[4096];
        for (int n; (n = in.read(b)) != -1; ) 
            out.append(new String(b, 0, n));
        
        return out.toString();
    


三,HttpClient简介

HttpClient是Apache开源组织提供的它是一个简单的HTTP客户端,不是浏览器,用于发送HTTP请求,接收HTTP响应。但不会缓存服务器的响应。它只是关注于如何发送请求、接收响应,以及管理HTTP连接。

HttpClient相比较来说简化了HttpURLConnection对象对Session、Cookie的处理。

可以说HttpClient就是一个增强版的HttpURLConnection,HttpClient可以做HttpURLConnection对象所有能做的事。

HttpClien中使用了连接池来管理持有连接,同一条TCP链路上,连接是可以复用的。HttpClient通过连接池的方式进行连接持久化。

HttpClient链接池的实现:
  1. 当有连接第一次使用的时候建立连接。
  2. 结束时对应连接不关闭,归还到池中。
  3. 下次同个目的的连接可从池中获取一个可用连接。
  4. 定期清理过期连接。
HttpClient连接池工具类封装

导入

				<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>

HttpClient工具类:

public class HttpClientUtil 
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    private volatile static CloseableHttpClient httpClient;

    private HttpClientUtil()

    private static CloseableHttpClient getHttpClient() 
        if (httpClient == null) 
            synchronized (HttpClientUtil.class) 
                if (httpClient == null) 
                    httpClient = HttpPoolConManager.getClient();
                
            
        
        return httpClient;
    


    /**
     * 自定义配置方式初使化httpclient
     * 此方法在一个应用系统里面,只需要执行一次。
     * 应用程序自行控制。
     * @param config
     */
    public static void config(HttpClientConfig config) 
        if (httpClient != null) 
            logger.warn("http client already init ! ");
        
        httpClient = java 通过HTTP请求模拟登陆,获取不到cookie,高手帮忙看一下,代码如下

每日一书|吃透HTTP原理,教你建立安全的HTTPS网站

使用URL设置ImageView

吃透Redis:网络框架篇-redis框架源码

吃透Redis:网络框架篇-redis框架源码

吃透Redis:网络框架篇-redis框架源码