HttpClient4.5源码泛读(1)

本篇探究HTTPClient客户端链接的最底层建立连接、发送请求、接收响应的原理,接下来的文章会从底层依次向上探究,本文尽可能少粘代码,只讲具体功能,如有错误请各位指正。

建立连接

在实现HttpClientConnectionOperator接口的DefaultHttpClientConnectionOperator类下,找到了客户端建立与服务器socket连接的代码,在调用该类之前已经已经保有了关于进行socket连接的一系列信息。

在DefaultHttpClientConnectionOperator类的connect方法中,完成了:

  1. 通过ConnectionSocketFactory,根据传入的socket参数(SocketConfig)来构建一个socket连接
  2. 把该链接托管给ManagedHttpClientConnection

    其中细节:

一:Lookup接口

  该接口实现的是通过传入一个参数,获得跟该参数有关的实例。
  在该类中用于获取ConnectionSocketFactory实例
    private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) {
        Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(
                SOCKET_FACTORY_REGISTRY);
        if (reg == null) {
            reg = this.socketFactoryRegistry;
        }
        return reg;
    }

可以看出,该方法尝试通过在HttpContext中根据SOCKET_FACTORY_REGISTRY常量获取一个保存有很多ConnectionSocketFactory实例的对象,在connect方法中

final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
        final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());

根据HttpHost的Scheme(刚读到此处不知其含义,可能是该host的名字,如baidu/google,也有可能是协议名http/ftp/file)来获取ConnectionSocketFactory实例。 Lookup的查找功能的实现是依靠的final map,在实现该接口的Registry类中可以看出。但具体如何将实例保存在map中、如何保证该map的持久性需要进一步阅读了解。

二:HttpClientConnection的socket连接托管

HttpClientConnection是一个客户端的链接(client-side connection)
对于已经生成的socket连接,需要在BHttpConnectionBase及其继承中进行操作。

BHttpConnectionBase提供了客户端的连接和服务器的连接通用的方法。

1:确保socket的线程安全

该类的所有对socket的操作(存、取、关闭、验证是否有效、取出socket的流等),均通过Java本身的AtomicReference(原子性引用)来操作保证其线程安全(基本原理移步http://www.tuicool.com/articles/RRbiye)。

绑定有效的socket进行数据传输:
把已经建立好的socket对象放入其属性AtomicReference中保证原子性操作

验证socket的有效性的具体实现如下:
首先,把socket的timeout设为1
然后调用在该socket中获取的SessionInputBufferImpl(该类在httpcore中)的fillBuffer方法。该方法用于判断在流中,当前读取位置之后是否还有数据(具体实现:校准读取位置,获取本次读取数据长度和已保存数据长度,read前两个长度的差是否为-1?返回-1:更新读取长度)。
该方法(isStable())为public,可以随时在socket中调用来询问是否有效。

2:发送请求、获得响应

HttpMessage包含了客户端向服务器的请求和服务器到客户端的响应相关内容。prepareInput方法把接收到的HttpMessage对象中的输入流、头信息等封装成HttpEntity用于请求。

它的子类 DefaultBHttpClientConnection 在父类的基础上,又完成了发送请求(头部、实体)和接收响应(头、实体)的功能。

//经过一系列处理之后 由ClientExecChain整合所有数据 并通过HttpRequestExecutor来发送请求获得响应

发送请求

在BHttpConnectionBase的继承DefaultBHttpClientConnection的构造方法中,获取了socket的输入流,并根据根据该输入流生成一个HttpMessageWriter

this.requestWriter = (requestWriterFactory != null ? requestWriterFactory :  DefaultHttpRequestWriterFactory.INSTANCE).create(getSessionOutputBuffer());

当sendRequestHeader时,调用了HttpMessageWriter的write方法,把已经处理好的request的信息输入到获取的socket的输入流中。至此请求已经发送完成。具体是否请求成功需要根据响应判断。

这里写图片描述

注:

 Request       = Request-Line
                 (( general-header
                 | request-header
                 | entity-header ) CRLF)
                 CRLF
                 [ message-body ]

HttpEntityEnclosingRequest

获取响应

1 获取头部

在DefaultBHttpClientConnection构造方法中,根据socket的输入流和相关参数MessageConstraints 生成了 HttpMessageParser。 该类用于Httpmessage(request、response)与socket的流内数据的转换

this.responseParser = (responseParserFactory != null ? responseParserFactory : DefaultHttpResponseParserFactory.INSTANCE).create(getSessionInputBuffer(), constraints);

在receiveResponseHeader时 调用AbstractMessageParser的parse方法。parse方法分两步完成响应头的封装

<1>.获取响应状态
在其子类DefaultHttpResponseParser的parseHead方法中:

final int i = sessionBuffer.readLine(this.lineBuf);

此处根据Response的构成发现,读的第一行(红色部分)是response的响应信息。

这里写图片描述

把响应信息写入linebuf中,对其进行格式处理,并把其作为response的响应状态信息封装response。

<2>.获取响应头信息

上一步完成之后,不断按行读取socket中的流,直到没有下一行或者是某行为空,把读取的信息解析成header封装到response中。

响应信息格式,按行分隔 头部和信息体之间有空格隔开

2 获取内容

根据status >= HttpStatus.SC_OK
&& status != HttpStatus.SC_NO_CONTENT
&& status != HttpStatus.SC_NOT_MODIFIED
&& status != HttpStatus.SC_RESET_CONTENT;

来判断响应是否有实体 若有的话
在连接中取出SessionInputBuffer,并根据头信息中内容长度来生成InputStream封装进Entity
判断实体长度是chunked(分段的)、identity(未给定大小)还是已经给定的大小。前两种暂时将Entity的ContentLength设为-1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值