一篇万字长文带你彻底学会http

前言

阅读本文大概需要20分钟。在这篇文章里可以学习到http的核心知识。在我们的面试中,http是我们常考的知识,索幸花点时间进行了整理。主要来自《图解Http》这本书。

目录

先把目录给大家看看。

一篇万字长文带你彻底学会http

http的诞生

最后我们来看应用层http(HyperText Transfer Protocol )。早在1989年,我们http就诞生了,CERN(欧洲核子研究组织 )设计了最初的理念:借助多文档之间相互关联形成的超文本 (HyperText),连成可相互参阅的 WWW(World Wide Web,万维 网)。 并且提出了3项www构建技术,把 SGML(Standard Generalized Markup Language,标准通用标记语言)作为页面的文本标 记语言的 HTML(HyperText Markup Language,超文本标记语言); 作为文档传递协议的 HTTP ;指定文档所在地址的 URL(Uniform Resource Locator,统一资源定位符)。

http协议主要由以下几个,可以参考https://coolshell.cn/articles/19840.html,很好的发展史。

概括下,http/0.9 1990问世,并不完善,然后出了http/1.0,添加了版本号,header,状态码,Content-Type等。但是缺点是网络性能,于是http/1.1修复了这个问题,还加入了新的功能。后面还有http/2,http/3。

编码

我们再来回顾一下:

原始的url值:

/data?cmd=Fence2Area&meta={"caller":"test","TraceId":"test"}&request={"fence":[{"lng":10.2,"lat":10.2},{"lng":10.2,"lat":8.2},{"lng":8.2,"lat":8.2},{"lng":8.2,"lat":10.2}],"coordtype":2}

编码后的url值:

/data?cmd=Fence2Area&meta={%22caller%22:%22test%22,%22TraceId%22:%22test%22}&request={%22fence%22:[{%22lng%22:10.2,%22lat%22:10.2},%20{%22lng%22:10.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:10.2}],%22coordtype%22:2}

在之前的报文拆解过程中,我们看到多了很多 %22,其实, 0x22是单引号 "的ascii值。

一方面,URL描述的资源为了能通过其他各种协议传送,但是有些协议在传输过程中会剥去一些特定的字符;另一方面,URL还是可读的,所以那些不可打印的字符就不能在URL中使用了,比如空格;最后,URL还得是完整的,它需要支持所有语言的字符。


总之,基于很多原因,URL设计者将US-ASCII码和其转义序列集成到URL中,通过转义序列,就可以用US-ASCII字符集的有限子集对任意字符或数据进行编码了。

转义的方法:百分号( %)后跟着两个表示ASCII码的十六进制数。比如:

一篇万字长文带你彻底学会http


URI和URL

我们需要在这里区分下URI和URL。URI 是 Uniform Resource Identifier 的缩写,Request For Comments(RFC2396)进行了定义。URI的一个例子

iftp://ftp.is.co.za/rfc/rfc1808.txt http://www.ietf.org/rfc/rfc2396.txt ldap://[2001:db8::7]/c=GB?objectClass?one mailto:John.Doe@example.com news:comp.infosystems.www.servers.unix tel:+1-816-555-1212telnet://192.0.2.16:80/ urn:oasis:names:specification:docbook:dtd:xml:4.1.2

URI 用字符串标识某一互联网资源,而 URL 表示资源的地点(互联 网上所处的位置)。可见 URL 是 URI 的子集。

http方法

HEAD方法只获取头部,不获取数据部分。通过头部可以获取比如资源的类型(Content-Type)、资源的长度(Content-Length)这些信息。这样,客户端可以获取即将请求资源的一些情况,可以做到心中有数。

POST用于向服务器发送数据,常见的是提交表单;PUT用于向服务器上的资源存储数据。

一篇万字长文带你彻底学会http


持久连接

我们知道http使用了TCP,每次建立连接需要经过三次握手,很少消耗资源,为了优化,引入了HTTP Persistent Connections,也称作HTTP keep-alive。持久连接的特点是,只要任意一端 没有明确提出断开连接,则保持 TCP 连接状态。

在 HTTP/1.1 中,所有的连接默认都是持久连接,但在 HTTP/1.0 内并 未标准化。

HTTP有keep-alive机制,目的是可以在一个TCP 连接上传输多个HTTP事务,以此提高通信效率。底层的TCP其实也有keep-alive机制,它是为了探测TCP连接的活跃性。TCP层的keepalive可以在任何一方设置,可以是一端设置、两端同时设置或者两端都没有设置。新建socket的时候需要设置,从而使得协议栈调用相关函数tcpsetkeepalive,来激活连接的keep-alive属性。

当网络两端建立了TCP连接之后,闲置(双方没有任何数据流发送往来)时间超过 tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对方的ack,如果一直没有收到对方的ack,一共会尝试 tcpkeepaliveprobes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试 tcp_keepalive_probes次后,依然没有收到对方的ack包,则会丢弃该TCP连接。TCP连接默认闲置时间是2小时,一般设置为30分钟足够了。

cookie

我们知道http是无状态的,请求和响应都没有序列化,所以我们引入了cookie技术。Cookie 技术通过在请求和响应报文中写入 Cookie 信 息来控制客户端的状态。

  • Name:就是cookieName
  • value:cookieName对应的值。
  • domain:域,表示当前cookie所属于哪个域或子域下面
  • path:表示cookie的所属路径,一般设为“/”,表示同一个站点的所有页面都可以访问这个cookie。
  • expiration:表示了cookie的有效期。
  • secure:表示该cookie只能用https传输。

cookie是服务器“贴在”客户端身上的标签,由客户端维护的状态片段,并且只会回送给合适的站点。有两类cookie: 会话cookie、持久cookie. 会话cookie在退出浏览器后就被删除了;而持久cookie则保存在硬盘中,计算机重启后仍然存在。

http报文

下面我们看下http的报文:

一篇万字长文带你彻底学会http


压缩

为了进行优化,我们可以进行压缩,常用的压缩有gzip,compress等。此外,在发送大文件的时候,我们可以把数据分块发送,分块传输编码(Chunked Transfer Coding) 。

主要有内容编码和传输编码。它与内容无关,它是为了改变报文数据在网络上传输的方式。传输编码是在HTTP 1.1中引入的一个新特性。通常,服务器需要先生成数据,再进行传输,这时,可以计算数据的长度,并将其编码到 Content-Length中。但是,有时,内容是动态生成的,服务器希望在数据生成之前就开始传输,这时,是没有办法知道数据大小的。这种情况下,就要用到 传输编码来标注数据的结束的。

HTTP协议中通过如下两个首部来描述和控制传输编码:


一篇万字长文带你彻底学会http


每个分块包含一个长度值(十六进制,字节数)和该分块的数据。 用于区隔长度值和数据。长度值不包含分块中的任何序列。最后一个分块,用长度值0来表示结束。注意报文首部包含一个 Trailer:Content-MD5, 所以在紧跟着最后一个报文结束之后,就是一个拖挂。其他如, Content-Length, Trailer, Transfer-Encoding也可以作为拖挂。


一篇万字长文带你彻底学会http

MIME类型

随着网络的发展,我们可能需要发送越来越多类型的对象,图片,视频都是可能的,是因为引入了MIME(Multipurpose Internet Mail Extensions).

响应数据中,我们注意到有一个首部:

  1. Content-Type: text/plain; charset=utf-8

互联网上有数千种不同的数据类型,HTTP给每种对象都打上了MIME(Multipurpose Internet Media Extension, 多用途因特网邮件扩展)标签,也就是响应数据中的 Content-Type. MIME本来是用在邮件协议中的,后来被移植到了HTTP中。浏览器从服务器上取回了一个对象时,会去查看MIME类型,从而得知如何处理这种对象,是该展示图片,还是调用声卡播放声音。MIME通过斜杠来标识对象的主类型和其中的特定的子类型,下表展示了一些常见的类型,其中的实体主体是指body部分:

状态码

此外,http的状态码也是十分重要的,先浏览下:

我们看几个常见的,先来看

  • 2XX 的响应结果表明请求被正常处理了200代表成功204代表处理成功,但是没有可返回的资源206 Partial Content 表示返回部分数据。
  • 3重定向类的,表明浏览器需要执行某些特殊的处理以正确处理请求。301 Moved Permanently,永久性重定向。302临时性重定向,注意和301对比,303See Other,注意区分,303 状态码和 302 Found 状态码有着相同的功能,但 303 状态码明确 表示客户端应当采用 GET 方法获取资源,还有304 Not Modified ,和重定向没关系307 Temporary Redirect
  • 4客户端错误的400 Bad Request ,表示请求报文中存在语法错误 ;401 Unauthorized403 Forbidden404 Not Found
  • 5服务器错误。500 Internal Server Error503 Service Unavailable

其中,我们需要理解重定向技术,非常有用的技术,对理解OAuth, CAS(SSO)很有帮助。

一个机器部署多个域名

这个话题到此结束,我们来看下一个话题,想想这么一个场景,我们可能有一个需求,在一台服务器上部署多个应用,比如a.com,b.com。用户访问后,经过DNS解析后,请求到了同一台服务器,怎么区分呢?

这个时候是不能用端口来区分的,原因在于都是www服务,使用的是同一个端口,那么怎么办呢?答案是必须在 Host 首部内完整指定主机名或域名的 URI。

代理

HTTP的代理服务器既是Web服务器,又是Web客户端。

区分:正向代理/反向代理。

使用代理可以“接触”到所有流过的HTTP流量,代理可以对其进行监视和修改。常见的就是对儿童过滤一些“成人”内容;网络工程师会利用代理服务器来提高安全性,它可以限制哪些应用层的协议数据可以通过,过滤“病毒”等数据;代理可以存储缓存的文件,直接返回给访问者,无需请求原始的服务器资源;对于访问慢速网络上的公共内容时,可以假扮服务器提供服务,从而提高访问速度;这被称为 反向代理;可以作为内容路由器,如对付费用户,则将请求导到缓存服务器,提高访问速度;可以将页面的语言转换到与客户端相匹配,这称为 内容转码器; 匿名代理会主动从HTTP报文中删除身份相关的信息,如 User-Agent, Cookie等字段。

缓存

当有很多请求访问同一个页面时,服务器会多次传输同一份数据,这些数据重复地在网络中传输着,消耗着大量带宽。如果将这些数据缓存下来,就可以提高响应速度,节省网络带宽了。

大部分缓存只有在客户端发起请求,并且副本已经比较旧的情况下才会对副本的新鲜度进行检测。最常用的请求首部是 If-Modified-Since, 如果在xx时间(此时间即为If-Modified-Since的值)之后内容没有变化,服务器会回应一个 304NotModified. 否则,服务器会正常响应,并返回原始的文件数据,而这个过程中被称为 再验证命中。

再验证可能出现命中或未命中的情况。未命中时,服务器回复 200OK,并且返回完整的数据;命中时,服务器回复 304NotModified; 还有一种情况,缓存被删除了,那么根据响应状态码,缓存服务器也会删除自己缓存的副本。

顺带提一句,若要在项目中使用缓存,就一定要关注缓存命中比例。若命中比例不高,就要重新考虑设置缓存的必要性了。

缓存服务器返回响应的时候,是基于已缓存的服务器响应的首部,再对一些首部字段做一些微调。比如向其中插入新鲜度信息(如 Age, Expires首部等),而且通常会包含一个 via首部来说明缓存是由一个缓存代理提供的。注意,这时不要修改 Date字段,它表示原始服务器最初构建这条响应的日期。

HTTP通过 文档过期机制和 服务器再验证机制保持已缓存数据和服务器间的数据充分一致。

文档过期通过如下首部字段来表示缓存的有效期:

一篇万字长文带你彻底学会http

当上面两个字段暗示的过期时间已到,需要向服务器再次验证文档的新鲜度。如果这时缓存仍和服务器上的原始文档一致,缓存只需要更新头部的相关字段。如上表中提到的 Expires字段等。

为了更好的节省网络流量,缓存服务器可以通过相关首部向原始服务器发送一个 条件GET请求, 这样只有在缓存真正过期的情况下,才会返回原始的文档,否则只会返回相关的首部。 条件GET请求会用到如下的字段:

一篇万字长文带你彻底学会http

其中,Last-Modified和ETag是很重要的。

如果在 If-Modified-Since 字段指定的日期时间后,资源发生了 更新,服务器会接受请求。首部字段 If-Modified-Since,属附带条件之一,它会告知服务器若 If- Modified-Since 字段值早于资源的更新时间,则希望能处理该请求。 而在指定 If-Modified-Since 字段值的日期时间之后,如果请求的资源 都没有过更新,则返回状态码 304 Not Modified 的响应。If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性。 获取资源的更新日期时间,可通过确认首部字段 Last-Modified 来确定。


只有在 If-None-Match 的字段值与 ETag 值不一致时,可处理 该请求。与 If-Match 首部字段的作用相反。首部字段 If-None-Match 属于附带条件之一。它和首部字段 If-Match 作用相反。用于指定 If-None-Match 字段值的实体标记(ETag)值与 请求资源的 ETag 不一致时,它就告知服务器处理该请求。

重定向

重定向分为http重定向和DNS重定向。

http重定向:服务器收到客户端请求后,向客户端返回一条带有状态码 302重定向的报文,告诉他们应该去其他的地方试试。

DNS重定向:DNS将几个IP地址关联到一个域上,采用算法决定返回的IP地址。可以是简单的 轮转;也可以是更高级的算法,如返回负载最轻的服务器的IP地址,称为 负载均衡算法;如果考虑地理位置,返回给客户端最近位置的地址,称为 邻接路由算法;还有一种是绕过出现故障的地址,称为 故障屏蔽算法。

http首部

http的报文首部是十分重要的,我们来看下:Connection也是比较重要的,主要有两个作用,控制不再转发给代理的首部字段 ,管理持久连接。

Date是时间。

ransfer-Encoding 指定了编码格式。

Accept 指定接收文件的格式;

Referer 告知服务器请求的原始资源的 URI。

User-Agent 用于传达浏览器的种类。

还有很多的,需要不断的学习。

https

HTTP+ 加密 + 认证 + 完整性保护 =HTTPS

认证方式

  • BASIC 认证(基本认证):Base64(用户名:密码),缺点是未加密
  • DIGEST 认证(摘要认证):可以防止密码窃听,无法防止用户伪装
  • SSL 客户端认证:需要收费
  • FormBase 认证(基于表单认证)用cookie来管理Session

消除http瓶颈之Websocket

Google 在 2010 年发布了 SPDY。想想下面的这些场景,海量的用户发布内容,为了实时的更新,客服端需要不断的和服务端进行通信,但是http存在着下面的制约:

  • 一条连接上只可发送一个请求。
  • 请求只能从客户端开始。客户端不可以接收除响应以外的指令。
  • 请求 / 响应首部未经压缩就发送。首部信息越多延迟越大。
  • 发送冗长的首部。每次互相发送相同的首部造成的浪费较多。
  • 可任意选择数据压缩格式。非强制压缩发送。

然后人们提出了Ajax(Asynchronous JavaScript and XML, 异 步 JavaScript 与 XML 技 术)技术,达到局部 Web 页面替换加载的异步通信手 段。和以前的同步通信相比,由于它只更新一部分页面,响应中传输 的数据量会因此而减少,这一优点显而易见。

Ajax 的核心技术是名为 XMLHttpRequest 的 API,通过 JavaScript 脚本 语言的调用就能和服务器进行 HTTP 通信。借由这种手段,就能从已 加载完毕的 Web 页面上发起请求,只更新局部页面。

而利用 Ajax 实时地从服务器获取内容,有可能会导致大量请求产 生。另外,Ajax 仍未解决 HTTP 协议本身存在的问题。

人们又提出了Comet技术,一旦服务器端有内容更新了,Comet 不会让请求等待,而是直接给客户端返回响应。这是一种通过延迟应答,模拟实现服务器端向客户端 推送(Server Push)的功能。通常,服务器端接收到请求,在处理完毕后就会立即返回响应,但为 了实现推送功能,Comet 会先将响应置于挂起状态,当服务器端有内 容更新时,再返回该响应。因此,服务器端一旦有更新,就可以立即 反馈给客户端。

内容上虽然可以做到实时更新,但为了保留响应,一次连接的持续时 间也变长了。期间,为了维持连接会消耗更多的资源。另外,Comet 也仍未解决 HTTP 协议本身存在的问题。

总结下:由拉变成了推。

我们可以看到,Ajax和Comet技术仍然存在着问题,我们推出了SPDY协议,想要消除http的瓶颈。我们来看下,SPDY 没有完全改写 HTTP 协议,而是在 TCP/IP 的应用层与运输层之 间通过新加会话层的形式运作。同时,考虑到安全性问题,SPDY 规 定通信中使用 SSL。

我们可以看到,使用 SPDY 后,HTTP 协议额外获得以下功能:

  • 多路复用流通过单一的 TCP 连接,可以无限制处理多个 HTTP 请求。所有请求 的处理都在一条 TCP 连接上完成,因此 TCP 的处理效率得到提高。
  • 赋予请求优先级SPDY 不仅可以无限制地并发处理请求,还可以给请求逐个分配优先 级顺序。这样主要是为了在发送多个请求时,解决因带宽低而导致响 应变慢的问题。
  • 压缩HTTP首部压缩 HTTP 请求和响应的首部。这样一来,通信产生的数据包数量和发送的字节数就更少了。
  • 推送功能支持服务器主动向客户端推送数据的功能。这样,服务器可直接发送 数据,而不必等待客户端的请求。
  • 服务器提示功能服务器可以主动提示客户端请求所需的资源。由于在客户端发现资源 之前就可以获知资源的存在,因此在资源已缓存等情况下,可以避免发送不必要的请求。

我们可以看到SPDY的确发挥了很大的作用,但是真的这么完美吗?

Web 的内容端不必做什么特别改动,而 Web 浏 览器及 Web 服务器都要为对应 SPDY 做出一定程度上的改动。有好 几家 Web 浏览器已经针对 SPDY 做出了相应的调整。另外,Web 服 务器也进行了实验性质的应用,但把该技术导入实际的 Web 网站却 进展不佳。因为 SPDY 基本上只是将单个域名( IP 地址)的通信多路复用,所 以当一个 Web 网站上使用多个域名下的资源,改善效果就会受到限 制。


由于上面问题的存在,我们引入了「使用浏览器进行全双工通信的 WebSocket」。

利用 Ajax 和 Comet 技术进行通信可以提升 Web 的浏览速度。但问题 在于通信若使用 HTTP 协议,就无法彻底解决瓶颈问题。WebSocket 网络技术正是为解决这些问题而实现的一套新协议及 API。当时筹划将 WebSocket 作为 HTML5 标准的一部分,而现在它却逐渐 变成了独立的协议标准。WebSocket 通信协议在 2011 年 12 月 11 日, 被 RFC 6455 - The WebSocket Protocol 定为标准。

我们先来看下Websocket的设计与功能:

WebSocket,即 Web 浏览器与 Web 服务器之间全双工通信标准。其 中,WebSocket 协议由 IETF 定为标准,WebSocket API 由 W3C 定为 标准。仍在开发中的 WebSocket 技术主要是为了解决 Ajax 和 Comet 里 XMLHttpRequest 附带的缺陷所引起的问题。

我们来看下这个协议:

一旦 Web 服务器与客户端之间建立起 WebSocket 协议的通信连接, 之后所有的通信都依靠这个专用协议进行。通信过程中可互相发送 JSON、XML、HTML 或图片等任意格式的数据。由于是建立在 HTTP 基础上的协议,因此连接的发起方仍是客户端, 而一旦确立 WebSocket 通信连接,不论服务器还是客户端,任意一方 都可直接向对方发送报文。我们来看其特点:

  • 推送功能:支持由服务器向客户端推送数据的推送功能。这样,服务器可直接发 送数据,而不必等待客户端的请求。
  • 减少通信量:只要建立起 WebSocket 连接,就希望一直保持连接状态。和 HTTP 相 比,不但每次连接时的总开销减少,而且由于 WebSocket 的首部信息 很小,通信量也相应减少了。为了实现 WebSocket 通信,在 HTTP 连接建立之后,需要完成一 次“握手”(Handshaking)的步骤。握手·请求:为了实现 WebSocket 通信,需要用到 HTTP 的 Upgrade 首部字段,告知服务器通信协议发生改变,以达到握手的目的。

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13

  • 握手·响应:对于之前的请求,返回状态码 101 Switching Protocols 的响应。HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
  • 静态服务器到动态服务器

    最初我们看到的内容都是别人编写好的html,当然静态的html不能满足我们的需求,我们希望服务器可以返回动态的内容。第一个解决方案是CGI,CGI(Common Gateway Interface,通用网关接口)是指 Web 服务器在接收到客户端发送过来的请求后转发给程序的一组机制。 比如php等语言。标准是RFC3875。

    当然,CGI没有一家独大,Java推出了Servlet(Server+Applet,表示轻量服务程序)。


    一篇万字长文带你彻底学会http

    我们可以看到CGI,由于每次接到请求,程序都要跟着启动一次。因此 一旦访问量过大,Web 服务器要承担相当大的负载。而 Servlet 运行 在与 Web 服务器相同的进程中,因此受到的负载较小。


    您可能还会对下面的文章感兴趣: