Java 基础(十六)网络编程

写了这么久 Java 代码,对网络编程的了解还停留在简单使用网络请求框架的阶段。
提及网络编程的知识点,好像大部分的东西也都知道,可是好像就知道一个专有名词的意思。好比说:
“URL 是什么?”
“URL 就是统一资源定位器”
“嗯?完了?”
“它就是一个专有名词啊”
“...”html

再好比说:
“你了解 http 吗?”
“哦,这个呀,我知道,超文本传输协议呀”
“嗯?还有吗?”
“...”
“那 https 呢?”
“这就是一个安全的超文本传输协议呀”
"..."java

网络编程嘛,说白了就是和服务器的一次通话/交互资源,提及来其实很简单,用起来好像也挺简单的。
如下是使用 PostMan 自动生成的一次网络请求代码,和咱们项目中的代码基本上差很少。node

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("http://gank.io/api/history/content/2/1")
  .get()
  .addHeader("cache-control", "no-cache")
  .addHeader("postman-token", "c9b415ce-4a35-097c-1a53-b12d526d8d97")
  .build();

Response response = client.newCall(request).execute();复制代码

这个过程真的很简单了,由于这特么是已经封装好的OkHttp,那么问题来了,这些参数到底都表示什么意思?参数都是以什么样的格式上传给服务器的?服务器怎么响应的?服务器响应的数据是什么格式?缓存、Token、性能优化、异常怎么处理?
今天,就和你们一块儿来探讨这些问题。
在探讨这些问题以前,得先作一些准备工做,首先咱们来学习如下几个知识点。android

  • Internet 地址
  • URL 和 URI
  • HTTP
  • URLConnection
  • HttpURLConnection

Internet 地址

原本标题是想用 IP 的,查了一下发现IP 是Internet Protocol(网络协议)的缩写,这个话题太大了,咱们简单了解一下 Internet 地址便可。chrome

咱们将全部链接到 Internet 的设备看作一个节点(node),计算机节点称为主机(host)。每一个节点或主机都由至少一个惟一的数来标识,这称为 Internet 地址,也就是咱们所说的 IP 地址。编程

IPv4和 IPv6

就是4个字节长度的ip 地址和6个字节长度的 ip 地址。
IPv6目前还没在世界范围内推广,你们记住由于 IPv4地址紧张不够用才引入 IPv6的概念便可。
好比 61.135.169.121就是百度首页服务器的 ip 地址。json

域名解析 DNS

刚刚我说了,61.135.169.121是百度服务器的地址,可是咱们访问百度的时候用的是 www.baidu.com,这个网址叫域名,一般访问的时候使用这个域名进行访问,访问的过程当中会去 DNS 服务器查找域名“www.baidu.com”所对应的 IP 地址“61.135.169.121”,而后经过 IP 地址访问服务器。设计模式

InetAddress

java.net.InetAddress 类是 Java 对 IP地址(包括 v4和 v6)的高层表示。大多数其余网络都要用到这个类,包括 Socket、ServerSocket、URP、DatagramSocket、DatagramPacket 等。通常来讲,它包括一个主机名和一个 IP 地址。
具体 api 就不细讲了,反正和 IP地址相关的问题,均可以来找这个类。
子类实现有 Inet4Address 和 Inet6Address。
大多数状况下,咱们不用考虑 v4仍是 v6,由于 v6还没普及,从代码层面来讲,用 v4仍是 v6,Java 已经帮咱们处理好了,做为应用层(OSI 模型的最高层)的咱们不须要关心这些。api

URL 和 URI

在上面咱们了街道如何经过主机名和 IP 地址肯定主机在 Internet 的地址(其实就是调用 InetAddress 的 api)。这里咱们继续学习如何肯定资源的地址。浏览器

HTML 是一个超文本标记语言,由于它提供了一种方法,能够知道 URL 标识的其余文档的连接。URL 能够惟一地标识一个资源在 Internet 上的位置。URL 是最多见的 URI(统一资源标识符)。URI 能够由资源的网络位置来标识资源,也能够由资源的名字、编号或其余特性来标识。

URL 类是 Java 程序在网络上定位和获取数据最简单的方法。你不须要考虑所使用协议的细节,也不用担忧如何与吴福气通讯。只要把 URL 告诉 Java,它就会为你得到数据。

URI 和 URL 的区别

统一资源标志符URI就是在某一规则下能把一个资源独一无二地标识出来。拿人作例子,假设这个世界上全部人的名字都不能重复,那么名字就是URI的一个实例,经过名字这个字符串就能够标识出惟一的一我的。
现实当中名字固然是会重复的,因此身份证号才是URI,经过身份证号能让咱们能且仅能肯定一我的。
那统一资源定位符URL是什么呢。也拿人作例子而后跟HTTP的URL作类比,就能够有:动物住址协议://地球/中国/浙江省/杭州市/西湖区/某大学/14号宿舍楼/525号寝/张三.人能够看到,这个字符串一样标识出了惟一的一我的,起到了URI的做用,因此URL是URI的子集。
URL是以描述人的位置来惟一肯定一我的的。在上文咱们用身份证号也能够惟一肯定一我的。对于这个在杭州的张三,咱们也能够用:身份证号:123456789来标识他。因此不管是用定位的方式仍是用编号的方式,咱们均可以惟一肯定一我的,都是URI的一种实现,而URL就是用定位的方式实现的URI。回到Web上,假设全部的Html文档都有惟一的编号,记做html:xxxxx,xxxxx是一串数字,即Html文档的身份证号码,这个能惟一标识一个Html文档,那么这个号码就是一个URI。而URL则经过描述是哪一个主机上哪一个路径上的文件来惟一肯定一个资源,也就是定位的方式来实现的URI。对于如今网址我更倾向于叫它URL,毕竟它提供了资源的位置信息,若是有一天网址经过号码来标识变成了http://741236985.html,那感受叫成URI更为合适,不过这样子的话还得想办法找到这个资源咯…

以上,摘抄自知乎。接下来,咱们来看看 URL 和 URI 的 api 吧

URI

如下,是从URI 源码里面摘选出来的九个重要字段

private transient String scheme;//方案
private transient String fragment;//特定于方案的部分
private transient String authority;//受权
private transient String userInfo;//用户信息
private transient String host;//主机
private transient int port = -1;//端口
private transient String path;//路径
private transient String query;//查询
private volatile transient String schemeSpecificPart;//片断复制代码

可能你们仍是不太理解 URI 是啥,其实就是将一个String地址分解成特定的属性。看看下图就能明白了~

URL

直接看图吧~~

URL 的 api 基本和 URI 差很少,大多都是构造方法和各个字段的get、set 方法。可是有个方法咱们须要注意一下。

  • public URLConnection openConnection()

URL 能够根据咱们传的参数直接建立一个通讯连接,咱们来简单看看是怎么建立的。

public URLConnection openConnection() throws java.io.IOException {
    return handler.openConnection(this);
}复制代码

在 URL 类里面找到 handler 的建立代码以下:

if (handler == null) {
    try {
            if (protocol.equals("file")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.file.Handler").newInstance();
            } else if (protocol.equals("ftp")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.ftp.Handler").newInstance();
            } else if (protocol.equals("jar")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.jar.Handler").newInstance();
            } 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();
            }
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }复制代码

这里根据 protocol 字段,建立了不一样的 handler,根据逻辑咱们能够看到建立了 HttpHandler,使用 HttpHandler 的 openConnection()方法建立了一个sun.net.www.protocol.http.HttpURLConnection 对象。

HTTP

超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个标准,定义了客户端如何与服务器对话,以及数据如何从服务器传回客户端。尽管一般认为 HTTP是一种传输 HTML 文件以及文件中内嵌图片的方法,但实际上 HTTP 是一个数据格式。它能够用来传输 TIFF 图片、Word 文档、exe 文件等。这里咱们将深刻后台,了解当咱们子安地址栏输入http://www.google.com并按下回车键时到底发生了什么。

HTTP 协议

HTTP 是客户端和服务器之间通讯的标准协议。HTTP 指定客户端与服务器如何创建链接、客户端如何从服务器请求数据,服务器如何响应请求,以及最后如何关闭链接。HTTP 链接使用 TCP/IP 来传输数据。对于从客户端到服务器的每个请求,都有4个步骤:

1.默认状况下,客户端在端口80打开与服务器的一个 TCP 链接,URL 中还能够指定其余端口。
2.客户端向服务器发生消息,请求指定路径上的资源。这个请求包括一个首部,可选的(取决于请求的性质)还能够有一个空行,后面是这个请求的数据。
3.服务器向客户端发生响应,响应以响应码开头,后面是包含数据的首部、一个空行以及所请求的文档或错误消息。
4.服务器关闭链接。

这是基本 HTTP1.0过程,在 HTTP1.1以及之后的版本中,能够经过一个 TCP 链接连续发送多个请求和响应。也就是说,在第一步和第四步直接,第二步和第三步能够反复屡次。另外,在 HTTP1.1中,请求和响应能够分为多个块发送。这样有更好的扩展性。

HTTP Request

每一个请求和响应都有一样的基本形式:一个首部行、一个包含元素数据的 HTTP 首部、一个空行,而后是一个消息体。

格式以下:

  • 请求行,分为三部分:请求方法、请求地址、协议版本

例如 GET/image/logo.gif HTTP/1.1,表示从/image目录下请求 logo.gif这个文件

  • 请求头,用于传递一些附加信息

常见通用请求 Header

名称 做用
Content-Type 请求体的类型,如:text/plain,application/json
Accept 说明接收的类型,能够多个值,用","隔开
Content-Length 请求体长度
Content-Encoding 请求体的编码格式,如 gzip、deflate
Accept-Encoding 告知对方我方接受的 Content-Encoding
Catche-Control 取值通常为 no-catche、max-age=xx(xx 为整数,表示自愿缓存 xx 秒)
  • 空行 请求头结束的标识符
  • 可选消息体

也就是三个部分,分别是:请求行、请求头、请求正文

请求方法:

HTTP/1.1协议中一共定义了八种方法来表面 Request-URI指定的资源的不一样操做方式:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT

这八种方法里面,如今咱们通常都只有 GET 和 POST 方法。

GET 和 POST 的区别:
1.GET 提交的数据会放在 URL 以后,以?分割 URL 和传输数据,参数之间以&相连。
2.GET 提交的数据大小有限制,最多只能有1024字节,而 POST 方法提交的数据没有限制
3.GET 方式须要使用 Requ.QueryString来取得变量的值,而 POST 方式经过 Request.Form来获取值
4.Get 方式提交数据,会带来安全问题,好比一个登录页面,经过 GET 方式提交数据时,用户名和密码将出如今 URL 上,若是页面能够被缓存或者其余人能够访问这台机器,就能够从历史记录获取该用户的帐号和密码。

HTTP Response

当客户端向服务器发生一个请求,服务器以一个状态做为相应,相应的内容包括:消息协议的版本、成功或者错误编码、服务器信息、实体元信息以及必要的实体内容。根据响应类别,服务器响应能够包含实体内容,但不是全部的响应都应有实体内容。

响应消息的结构也分为三部分

  • 响应状态行
    主要包含 HTTP 版本信息(通常是1.1)和状态码,状态码对于信息以下所示:
状态码 对应信息
1xx 提示信息-表示请求已接受,继续处理
2xx 用于表示请求已经被成功接收、处理、
3xx 用于表示自愿被永久转到其余 URL,也就是重定向
4xx 客户端的错误-请求有语法错误或没法实现
5xx 服务器端错误-服务器未能实现合法的请求
  • 响应头

响应头一样可用于传递一些附加信息。
常见响应 Header

名词 做用
Date 服务器日期
Last-Modified 最后被修改时间
Transfer-Encoding 取值通常为 chunked,通常出如今响应体长度不能肯定的状况下
Set-cookie 设置 Cookie
Location 重定向到另外一个 URL
Server 后台服务器
  • 响应体

响应体也就是咱们须要的内容,通常在相应头中会用 Content-Length来明确相应体的长度,便于浏览器接受,对于大数据量的正文消息,也会使用 chunked 的编码方式。

HTTPS

简介

HTTPS(Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的 http 通道,简单的讲就算 HTTP 的安全版。即HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,所以加密的详细内容就须要 SSL。

HTTPS 和 HTTP 的区别

1.https 须要到 ca 申请证书,通常免费证书不多,须要交费。
2.http 是超文本传输协议,信息是明文传输;https 则是具备安全性的 ssl 加密传输协议。
3.http 和 https 室友彻底不一样的链接方式,用的端口也不同,前者是80,后者是443.
4.http 的连接很简单,无状态的。https 协议是由ssl+http 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。

https 的做用

它的主要做用能够分为两种:一种是创建一个信息安全通道,来保证数据传输安全;另外一种是确认网站的真实性。

1.通常意义上的 https,就是服务器有一个证书。主要目的是保证服务器就是他声称的服务器,这个跟第一点同样;服务端和客户端之间的全部通信,都是加密的。
2.具体讲,是客户端产生的一个对称的秘钥,经过服务器的证书来交换秘钥,即通常意义上的握手过程。
3.接下来全部的信息往来都是加密的。即便第三方截取,也没有任何意义,由于他没有秘钥,固然篡改也就没有什么意义了。
4.少量对客户端有妖气的状况下,会要求客户端也必须有一个证书。

这里的客户端证书,其实就相似表示我的信息的时候,除了用户名/密码,还有一个 CA 认证过的身份。由于我的证书通常来讲是别人没法模拟的,全部这样可以更深的确认本身的身份。少数我的银行的专业版是这种作法,好比 U 盾。

SSL 就不讲了吧,涉及的东西太多了。

URLConnection

URLConnection 是一个抽象类,表示指向 URL 指定资源的活动链接。URLConnection 有了两个不一样但相关的用途。首先,与 URL 类相比,它对与服务器(特别是 HTTP 服务器)的交互提供了更多的控制。URLConnection 能够检查服务器发送的首部,并相应地作出响应。它能够设置客户端请求中使用的首部字段。最后,URLConnection 能够用 POST、PUT 和其余 HTTP 请求方法向服务器发生数据。

打开 URLConnection

直接使用URLConnection 类的程序遵循如下基本步骤:

1.构造一个 URL 对象
2.调用这个 URL 对象的 openConnection()获取一个对应该 URL 的 URLConnection 对象。(如 http 请求获取的是 HttpURLConnection)
3.配置这个 URLConnection
4.读取首部字段
5.获取输入流并读取数据
6.获取输出流并写入数据
7.关闭链接

并不必定执行全部这些步骤。例如,若是某种 URL 的默认设置是能够接受的,那么可能会跳过步骤3。若是只须要服务器的数据,不关心任何元信息,或者协议不提供任何元信息,那么能够跳过步骤4。若是只但愿接受服务器的数据,而不向服务器发生数据,就能够跳过步骤6.依据不一样协议,步骤5和6的顺序可能会反过来或者交替出现。

URLConnection 类仅有一个构造函数为保护类型。

protected RULConnection(RUL url)复制代码

所以,除非派生 URLConnection 的子类来处理新的 URL 类型,不然要经过调用 URL 类的 openConnection()方法来建立这样一个对象。

try{
    URL u = new URL("http://www.baidu.com");
    URLConnection uc = u.openConnection();
}catch(Exception e){
    System.out.println(e);
}复制代码

URLConnection类声明为抽象类。不过,除了一个方法外,其他方法都已经实现。你会发现覆盖这个类的其余方法很方便,或者可能颇有必要。必须由子类实现的一个方法是 connect(),他创建与服务器的链接,于是依赖于服务类型(HTTP,FTP 等)。例如 sun.net.www.protocol.http.HttpURLConnection的 connect()方法会建立一个sun.net.www.http.HttpClient对象,由他负责链接服务器。

第一次构造 URLConnection 时,它是未链接的。也就是说,本地和原创主机没法发送和接收数据。没有 sockey 链接这两个主机。connect()方法在本地和远程主机之间创建一个链接(通常使用 TCP socket,但也可能经过其余机制来创建),这样就能够收发数据了,不过,对于 getInputStream()、getContent()、getHeaderFiled()和其余要求打开链接的方法,若是链接还没有打开,他们就会调用 connect()。所以,你不多须要直接调用 connect()。

读取服务器的数据

下面是使用 URLConnection 对象从一个 URL 获取数据所需的最起码的步骤:

1.构造一个 URL 对象。
2.调用这个 URL 对象的 openConnection()方法,获取对应的 URLConnection。
3.调用这个 URLConnection 的 getInputStream()方法
4.使用一般的流 API 读取输入流。

getInputStream() 方法返回一个通用的 InputStream,能够读取和解析服务器发送的数据。如下示例使用 URLConnection 下载一个 Web 页面:

对于这个例子中这样一个简单的输入流,URL 和 URLConnection 直接的区别并不明显。
这两个类的最大不一样在于:

  • URLConnection 提供了对 HTTP 首部的访问
  • URLConnection 能够配置发送给服务器的请求参数
  • URLConnection 除了读取服务器数据外,还能够向服务器写入数据

读取首部

HTTP 服务器在每一个响应前面的首部中提供了大量信息。例如,下面是一个服务器返回的典型的 HTTP 首部:

bdpagetype →1
bdqid →0xef0ab4fd0001728d
bduserid →0
cache-control →private
connection →Keep-Alive
content-encoding →gzip
content-type →text/html; charset=utf-8
cxy_all →baidu+631df64ada7e1077f47313160f7a4090
date →Thu, 16 Nov 2017 08:20:15 GMT
expires →Thu, 16 Nov 2017 08:19:54 GMT
p3p →CP=" OTI DSP COR IVA OUR IND COM "
server →BWS/1.1
strict-transport-security →max-age=172800
transfer-encoding →chunked
vary →Accept-Encoding
x-powered-by →HPHP
x-ua-compatible →IE=Edge,chrome=1复制代码

这里有大量信息,haha,我不少都不认识,具体字段可对照我上文列的那个响应头字段表。

URLConnection 有几个获取经常使用响应头字段的方法

  • getContentType() 获取响应主体的 MIME 内容类型
  • getContentLength() 获取内容中有多少字节
  • getContentEncoding()获取内容的编码格式
  • getDate() 获取内容返回时间
  • getLastModified() 获取最近修改时间
  • getExpires() 表示文档过时时间,过时后须要从新下载
  • getHeaderField(String name)获取首部的任意字段

缓存

Web 浏览器多年来一直在缓存页面和图片。若是一个 logo 在网站的每个页面上重复出现,浏览器通常只会从远程服务器上加载一次,将它保存在缓存上,每次须要的时候会从缓存从新加载,而不是每次遇到这个 logo 都请求远程服务器加载。一些 HTTP 首部(包括 Expires 和 Cache-control)能够控制缓存。

默认状况下,通常认为使用 GET 经过 HTTP 访问的页面能够缓存,也应当缓存。使用 HTTPS 或 POST 访问的页面不该缓存。不过 HTTP 首部能够对此做出调整:

  • Expires 首部(主要针对 HTTP1.0)指示能够缓存这个资源表示,直到指定的世界为止。
  • Catche-control 首部(HTTP1.1)提供了细粒度的缓存策略:

    • max-age=[seconds]:从如今直到缓存项过时以前的秒数
    • s-maxage=[seconds]:从如今起,直到缓存项在共享缓存中过时以前的秒数。私有缓存能够将缓存项保存更长时间
    • public:能够缓存一个通过认证的响应。不然已认证的响应不能缓存。
    • private:仅耽搁用户缓存能够保存响应,而共享缓存不该保存。
    • no-catch:这个策略的做用与名字不太一致。缓存项仍然能够缓存,不过客户端在每次访问时要用一个 Etag 或 Last-modified 首部从新验证响应的状态。
    • no-store:无论怎样都不缓存。

      若是 Catch-control 和 Expires 首部都出现,Cache-control 会覆盖 Expires。服务器能够在一个首部中发送多个 Cache-control 首部,只要它们没有冲突

  • Last-modified 首部指示资源最后一次修改的日期。客户端能够用一个 HEAD 请求来检查这个日期,只要当本地缓存的副本遭遇 Last-modified 日期时,它才会真正执行 GET 来获取资源。

  • Etag 首部(HTTP1.1)是资源改变时这个资源的位移标识符。客户端可使用一个 HEAD 请求来检查这个标识符,只有当本地缓存的副本有一个不一样的 Etag 时,它才会真正执行 GET 来获取资源。

配置链接

URLConnection 类有7个保护的实例字段,定义了客户端如何向服务器作出请求。这些字段包括

protected URL url;
protected boolean doInpt = true;
protected boolean doOutput = false;
protected boolean allowUserInteraction = defaultAllowUserInteraction;
protected useCaches = defaultUserCachesl;
protected long ifModifiedSince = 0;
protected boolean connected = false;复制代码

例如,若是 doOutput 为 true,那么除了经过 URLConnection 读取数据外,还能够经过将数据写入到服务器。若是useCaches为 false,链接会染过全部本地缓存,从新从服务器下载文件。

因为这些字段是保护字段,因此要读写这些字段只能经过 get、set 方法。

只能在 URLConnection 链接以前修改这些字段。对于设置字段的方法,若是调用这些方法时链接已经打开,大多数会抛出一个 IllegaStateException 异常。通常状况下,只能在链接打开以前设置 URLConnection 对象的属性。

  • protected URL url

url 字段制定了这个 URLConnection 链接的 URL。构造函数会在建立 URLConnection 时设置这个字段,此后不能再改变。能够经过调用 getURL()方法获取这个字段的值。

  • protected boolean connected

若是链接已经打开,boolean 字段 connected 为 true,若是链接关闭,这个字段则为 false。因为在建立一个新 URLConnection 对象时链接还没有打开,因此初始值为 false。

  • protected boolean allowUserInteraction

有些 URLConnection 须要与用户交互。例如,Web 浏览器可能须要用户名和口令。不过,不少应用程序不能假定真实存在一个能够与它交互的用户。不过好像咱们 Android 开发用不上。

  • protected boolean doInput

URLConnection 能够用于读取服务器、写入服务器,或者同时用于读/写服务器。若是 URLConnection 能够用来读取,保护类型 boolean 字段 doInput 就为 true,不然为 false。默认值是 true。

  • protected boolean doOutput

程序能够用 URLConnection 将输出发回服务器。例如,若是程序须要使用 POST 方法向服务器发生数据,能够经过从 URLConnection 获取输出流来完成。若是 URLConnection 能够用于写入,字段 doOutput 就为 true,不然为 false。

  • protected boolean ifModifySince

许多客户端会保留之前获取的文档缓存。若是用户再次要求相同的文档,能够从缓存中获取。不过,在最后一次获取这个文档以后,服务器上的文档可能会改变。要判断是否有变化,惟一的办法就是询问服务器。客户端能够在户请求 HTTP 首部中包括一个If-MOdified_since。这个首部包括一个日期和时间。若是文档在这个时间以后有所修改,服务器就发送该文档,不然就不发生。通常状况下,这个世界是客户端最后得到文档的时间。

  • protected boolean useCaches

有些客户端能够从本地缓存获取文档,而不是从服务器获取。applet 能够访问浏览器的缓存。独立的应用程序能够用 java.net.ResponseCache类。若是有缓存,useCaches 变量肯定了是否可使用缓存。默认值为 true,表示将使用缓存。

超时

public void setConnectionTimeout()

调用这个方法设置链接超时。

配置客户端请求 HTTP 首部

HTTP 客户端(如浏览器)向服务器发生一个请求行和一个首部。

服务器能够根据请求信息向客户端返回不一样的数据,获取和设置cookie,经过口令认证用户等。经过再客户端发送和服务器响应的首部中放置不一样的字段,就能够完成这些工做。

  • public void setRequestProperty(String name,String value)

setRequestProperty()方法指定的名和值为这个 URLConnection 的首部加一个字段。这个方法只能在链接打开以前使用。重复调用这个方法会覆盖以前的字段。若是要增长一对,须要使用 addRequestProperty()方法。

并无一个固定的合法首部列表。服务器通常会忽略没法识别的首部。HTTP 确实对首部字段的名和值有一些限制。例如,名不能包含空白符,值不能包含任何换行符。若是一个字段包含换行符,会抛出 IllegalArgumentException 异常。

向服务器写入数据

有时候须要向URLConnection 写入数据,例如,使用 POST 向服务器提交表单,或者使用 PUT 上传文件。getOutputStream()方法返回一个 OutputStream,能够用来写入数据传给服务器:

public OutputStream getOutputStream()复制代码

因为 URLConnection 在默认状况下不容许输出,因此在请求输入流以前必须调用 setDooutput(true)。为一个 http URL 将 doOutput 设置为 true 时,请求方法将由GET 变为 POST。

猜想 MIME 媒体类型

  • public static String guessContentTypeFromName(String var0)

经过文件扩展名判断,通常来讲没有问题

  • public static String guessContentTypeFromStream(InputStream var0)

经过尝试查看流中前几个字节来猜想内容类型,猜想结果一般没有根据扩展名猜想靠谱。

HttpURLConnection

java.net.HttpURLConnection类是 URLConnection 的抽象子类。它提供了另一些方法,在处理 http RUL 时尤为有帮助。具体的,它包含的方法能够得到和设置请求方法、肯定是否重定向、获取响应码和消息,以及肯定是否使用了代理服务器。

请求方法

当客户端联系一个服务器时,它发送的第一个内容是请求行。通常状况下,这一行以 GET 开头,后面是客户端但愿获取的资源路径,以及 HTTO 版本。

一般状况下,默认请求方法是 GET,能够经过如下方法进行修改

  • public void setRequestMethod(String method)

请求的方法有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE,通常咱们 Android 都只用 GET 和 POST,可是如今好像后台都比较喜欢统一用 POST 了。

断开与服务器的链接

HTTP1.1支持持久链接,容许一个 TCP socket 发送多个请求和响应。服务器不会由于已经向客户端发送了最后一个字节的数据就当即关闭链接。也就是说,在服务器关闭链接以前,若是再链接同一个服务器,它会重用 socket。手动调用 disconnect()方法会主动断开链接,若是还有打开的流,也会关闭。可是反过来关闭打开的流,并不会关闭链接。

处理服务器响应

前面咱们介绍过服务器响应的格式,这里就再也不赘述。

  • getResponseCode() 获取响应状态码
  • getResponseMessage()获取响应码后面的文本字符串

重定向

300一级的响应吗都表示某种重定向,即请求的资源再也不指望的位置上,但有可能会在其余位置找到。遇到这样的响应时,大多数浏览器会自动重新位置加载文档。

默认状况下,HTTPURLConnection 会跟随重定向,不过 HTTPURLConnection 有两个静态方法能够控制是否跟随重定向。

  • public static boolean getFollowRedirects()
  • public static void steFollowRedirects(boolean follow)

注意,这里是静态方法,全局修改。

结束语

可能这一章写的比较凌乱,其实我也很绝望,不少东西我也是一边学一边记录的,这已是整理的第三版笔记了。

这一章学习的主要目的是为了对整个 HTTP请求有个大体的了解,不少知识点都是一笔带过(还忽略了一些),同窗们知道有这么回事就好了,大概在十8、十九课的时候,我会和你们一块儿用咱们学过的知识点,手撸一个 Volley 网络请求架构。

我提早透露一下此次手撸的 Volley会实现哪些需求以及用到了哪些知识点。

实现的需求

  • 支持请求 JSON 文本类型学,音频,图片类型,批量下载。上传~
  • 请求各类数据时,调用层不用关心上传参数的封装
  • 获取数据后,调用层不用关心 JSON 数据的解析
  • 回调时,调用层只须要知道传入的 JSON 的对应响应类
  • 回调响应结果发生在主线程(线程切换)
  • 对下载,上传扩展
  • 支持高并发请求,请求队列一次获取,能够设置最大并发数,设置先请求先执行

会用到的知识点

  • 泛型
  • 请求队列
  • 阻塞队列
  • 线程拒绝策略

用到的设计模式

  • 模板方法模式
  • 单例模式
  • 策略模式
  • 生产者消费者模式(不属于23种基本的消费模式 haha)
相关文章
相关标签/搜索