HttpURLConnection的实现(原理基本层)

2024-06-01 17:48

本文主要是介绍HttpURLConnection的实现(原理基本层),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在java进行网络请求的时候,使用的基本单位请求工具,就是最常用的 HttpURLConnection,有人是这是java标准库提供的基本小部件(whatever)。在进行高性能,高可用性选型的时候,很有必要,对HttpURLConnection最更加底层的了解。

        首先,在使用层面,无论使用什么网络框架,对于缓存,请求超时配置,请求体数据体的配置,https证书信任,等层面的东西,在工具框架库的层面,大同小异,只是封装的模式,提供的功能差异,不够根本的差距。

        深层次一点,在进行socket请求时,请求的重试,重定向,socket连接池,协议的支持等层面,会更深一层次反应,这个框架基础的性能。是的,这里,我们仍然都是在sokect的基础上的。

*******************************下面,剖析HttpURLConnection的本来面目******************

        这个类的使用,这里不说了,网上到处都是,怎么做数据的设置提交,和数据的接收和关闭等等。

首先 HttpURLConnection 和 HttpsURLConnection 都是抽象类。

abstract public class HttpURLConnection extends URLConnection {}abstract public class HttpsURLConnection extends HttpURLConnection{}
        HttpURLConnection,和HttpsURLConnection只是做了http状态码的定义,默认head定义,和ssl工厂,hostName审核器的判断定义。真真正正的 connect() 的实现,都是没有的。也就是说,这两个类抽象的部分,就是网络请求的根本部分。这样确实有解耦的作用。
    通过源码,我们找到,HttpURLConnection实例对象的获得。

URLStreamHandler handler;
public URLConnection openConnection() throws java.io.IOException {return handler.openConnection(mUrl);}
//这个静态方法,是获得对URL流处理器的根本,这里面,实现了URLStreamHandler的单例化,运行时只需要获得一次
static URLStreamHandler getURLStreamHandler(String protocol) {
//这里面,有一个工厂生成,这个工厂,可以通过外部实现接口,
handler = factory.createURLStreamHandler(protocol);
}
        这个用来生成URLStreamHandler的工程可以通过setURLStreamHandlerFactory设置。但是我们默认情况下,都是采用标准库的自己的实现。抽象类 URLStreamHandler 是所有流协议处理程序的通用超类.

        这里才是整片文档的关键

            if (handler == null) {final String packagePrefixList = System.getProperty(protocolPathProp,"");StringTokenizer packagePrefixIter = new StringTokenizer(packagePrefixList, "|");while (handler == null && packagePrefixIter.hasMoreTokens()) {String packagePrefix = packagePrefixIter.nextToken().trim();try {String clsName = packagePrefix + "." + protocol +".Handler";Class<?> cls = null;try {ClassLoader cl = ClassLoader.getSystemClassLoader();cls = Class.forName(clsName, true, cl);} catch (Exception ignored) {
			//删除了一些异常处理,这里看主体。代码在URL.java中。}}}

从代码看出来,jdk把具体的实现,交给了虚拟机的运行时。

java.protocol.handler.pkgs,这个虚拟机环境变量。这个值,可以通过命令改变,从而改变网络协议的具体实现。java.protocol.handler.pkgs=com.acme.protocoljava.protocol.handler.pkgs=com.acme.protocol|com.acme.protocol2
通过上面的代码块可以看出来,会默认优先加载前面的可加载类类的命名模式为 [package_path].[protocol].Handler

    而可用来实现handler的类名称数据,是在编译是的一个类路径下。我们可以根据工厂有自己的协议实现,也可以通过这种热加载机制,在虚拟器启动时,改变虚拟机java.protocol.handler.pkg这个环境变量key的值。
    如果根据这个环境变量,还是没有能够加载URLStreamHandler的类,jdk1.8默认有几个固定的判断。

            if (handler == null) {try {// BEGIN Android-changed// Use of okhttp for http and https// Removed unnecessary use of reflection for sun classesif (protocol.equals("file")) {handler = new sun.net.www.protocol.file.Handler();} else if (protocol.equals("ftp")) {handler = new sun.net.www.protocol.ftp.Handler();} else if (protocol.equals("jar")) {handler = new sun.net.www.protocol.jar.Handler();} else if (protocol.equals("http")) {handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpHandler").newInstance();} else if (protocol.equals("https")) {handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpsHandler").newInstance();}// END Android-changed} catch (Exception e) {throw new AssertionError(e);}}
这里有关于这种机制的讨论: https://accu.org/index.php/journals/1434

这里,找到两个实现,可以看一下:

     1>.HttpConnection对于网络的实现,是默认外包的,外包方大多取决于定制的平台,大多还是sun自己httpClient的子孙或者嫡系,这个我没有找到关于这个的实现。
        在rt.jar包中有一种实现(基本上可以把这个作为大多数情况下jdk的实现)。sun.net.www.protocol.https.HttpsURLConnectionImpl(DelegateHttpsURLConnection):
        

sun.net.www.protocol.http.HttpURLConnection extends java.net.HttpURLConnection{public void connect() throws IOException {synchronized(this) {this.connecting = true;}this.plainConnect();//这里首先会进行 URLtoSocketPermission 检查(只是协议主机路径等的合法性)}
}
    上面可以看到sun.net.www.protocol里有一套具体的请求实现,
        
plainConnect0();//此方法首先会使用cacheHandler设置cachedResponse,作为部分字段。
//然后这个方法包含走代理的逻辑。instProxy作为代理。
//最后无论是否会使用代理或者代理的层级,都会动过HttpClient.New()方法使用HttpClient.
        通过调用getInputStream()->getInputStream0也会间接地调用getOutputStream(),而调用getOutputStream()则会间接使用getOutputStream0()使用HttpClient  http.getOutputStream()方法。最终的请求都是HttpClient的父类NetworkClient所做,根据传递的信息配置和使用 InetSocketAddress。

    2>.下面是OKHttp的实现,这个就不用多说了,利用Okhttp框架的源码部分,新版本系统已经作为默认的实现:

public final class HttpHandler extends URLStreamHandler {@Override protected URLConnection openConnection(URL url) throws IOException {return new OkHttpClient().open(url);}@Override protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {if (url == null || proxy == null) {throw new IllegalArgumentException("url == null || proxy == null");}
//‘com.squareup.okhttp:okhttp:1.5.0’return new OkHttpClient().setProxy(proxy).open(url);//开放出来的Builder类并没有open方法。}@Override protected int getDefaultPort() {return 80;}
}

//上面对网络请求,会发到excute上。。。后面的东西就是OKHttp的部分了。
execute(boolean readResponse) throws IOException {// 调用了HttpEngine的sendRequest方法。httpEngine.sendRequest();route = httpEngine.getRoute();handshake = httpEngine.getConnection() != null ? httpEngine.getConnection().getHandshake(): null;
}
    整体就是:HttpURLConnection的抽象方法connect等  <--需要URLStreamHandler来产生 <--handler需要自定义的工厂创建或者jdk自己实现的

网上,别人的文档,来源未知:

Android 2.2版本之前,bug比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:
在Android 4.0版本中,我们又添加了一些响应的缓存机制。当缓存被安装后(调用HttpResponseCache的install()方法),所有的HTTP请求都会满足以下三种情况:
比较轻便,灵活,易于扩展
在3.0后以及4.0中都进行了改善,如对HTTPS的支持
在4.0中,还增加了对缓存的支持
在android 2.2及以下版本中HttpUrlConnection存在着一些bug,所以建议在android 2.3以后使用HttpUrlConnection,2.3之前使用HttpClient。

这篇关于HttpURLConnection的实现(原理基本层)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1021699

相关文章

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分