《图解HTTP》读书笔记

本文为作者原创内容,未经许可,禁止转载。如您发现侵权行为,请联系我们

首先奉上该书的PDF:《图解HTTP》


协议简介:

计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议(protocol)。


TCP/IP协议族以及协议族分层:
所以在互联网中存在着各种各样的协议,每一步的通信都需要协议,把互联网相关联的协议集合起来总称为TCP/IP。

TCP/IP协议族按层次分为:应用层、传输层、网络层、数据链路层。分层的好处是,在某个地方需要改变设计时,不需要更新整个连接路线上的协议,只需要把变动的层替换掉即可,这样设计就简单了,每个层次只需要完成分派给自己的任务就行。应用层(HTTP、FTP、DNS)



利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通信。发送端从应用层往下走,接收端则往应用层往上走。

我们用 HTTP 举例来说明,首先作为发送端的客户端在应用层(HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。接着,为了传输方便,在传输层(TCP 协议)把从应用层处收到的数据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP请求。

发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。这种把数据信息包装起来的做法称为封装(encapsulate)。    


IP、TCP和DNS协议的简单介绍:

IP:IP位于网络层,IP协议的作用就是把数据包传送给对方,而确保数据包传送的准确性,有两个重要的条件,IP地址MAC地址。
TCP:TCP位于传输层,用于提供可靠的字节流服务,即是为了方便传输,将大块数据分割成以报文段为单位的数据包进行管理,而且TCP协议能够确认数据最终是否送达给对方。

TCP为了准确无误地将数据送达目标处,采用了三次握手策略,具体过程如下:

首先,TCP握手的时候会使用TCP的标志,SYN和ACK


发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束。


DNS:

DNS服务和HTTP协议一样位于应用层,提供域名到IP地址的解析服务。



  • 在两台计算机之间使用HTTP协议进行通信时,在一条通信线路上必定有一端是客户端,另一端是服务端。
  •  http协议规定,请求从客户端发出,服务端进行响应返回,也就是说,服务端在没有收到请求之前不会发送响应。
  •  请求报文是由请求方法、请求URL、协议版本、可选的请求首部字段和内容实体构成。如:


  •  响应报文是由协议版本、状态码、用来解释状态码原因的短语、可选响应首部以及实体主体构成。

HTTP是不保存状态的协议:

http不对请求和响应之间的通信状态进行保存,即是协议对发送过的请求或响应不做持久化处理,也就是说服务器不知道上次是否接收到同一客户端的请求,以及是否响应等。所以,每次HTTP的请求,不保留之前的一切信息,这么做的目的是为了更快的处理大量事务,确保协议的可伸缩性,如果每个请求都记下状态,增加CPU的开销。但是随着web不断的发展,这种方式遇到了问题,在一个网站中,某些页面必须是登录之后才能看的,客户端登录之后,需要保持登录状态。

HTTP/1.1虽然是无状态协议,但是为了应用上述场景引入了cookie技术,这样通信时就可以管理状态了。

cookie技术通过在请求和响应报文中写入cookie信息来控制客户端的状态。cookie会根据服务器端响应报文中的Set-Cookie的首部字段信息,通知客户端保存cookie,当下次客户端再往服务器发送请求时,客户端会自动在请求报文中加入cookie值后发送出去。服务器端发现客服端发送过来的cookie后,回去检查究竟从哪一个客服端发送过来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。发送的报文信息如下图:

 在HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP连接。如下图:



 以当年通信情况来看,都是些容量很小的文本传输,这样做也没有什么问题,但随着HTTP的普及,文档中包含了大量的图片。如现在的一个网页中包含了很多张图片,所以每次请求都会造成无所谓的TCP连接建立和断开,增加通信量的开销。为了解决这个问题,HTTP/1.1想出了持久连接,也称为http keep-alive方法,特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态,如下图:


这样做的好处是:减少了TCP连接的重复建立和断开造成的额外开销,减轻了服务器端的负载。在HTTP/1.1中,所有的连接默认都是持久连接。

tip:

  1. 持久连接是“管线化”成为可能,即不用等待响应发送下一个请求,可以做到同时并行发送多个请求
  2. 持久连接可以让请求更快结束,而管线化技术则比持久连接还要快,请求数量越多,时间差越明显。


  • 当待发送邮件增加附件时,为了使邮件容量变小,会压缩传输内容,常见的内容编码有:gzip(gun zip),compress(unix系统的标准压缩),deflate(zlib),identity(不进行编码)

  • HTTP的几种传输方式:
上述的编码请求。还有分块传输,即把主体实体分成多个部分,然后由客服端解码,恢复到编码前的实体主体。还有传输多部分对象集合,例如文本,视频,图片,还有范围传输,对一份10000字节大小的资源,如果使用范围请求,可以只请求5001 - 10000 字节内的资源,请求时会使用range首部字段来指定范围针对范围请求,响应会返回状态码206


  • 告知服务器意图的HTTP方法
  get方法 主要是获取响应主体内容
  post方法 用来传输实体主体 其他方法如下图:

HTTP的报文:

  • 请求报文与响应报文的结构:

  • http返回状态码的介绍:
200:从客户端发来的请求都被服务器端正常处理了。
204:服务器端已经接收并成功处理,但在响应报文中不含实体的主体部分。一般是客户端往服务端发送信息,而对客户端不需要发送新信息内容情况下使用。
206:客户端进行了范围请求,而服务器端成功执行了这部分的GET请求。

301:表示请求的资源已被分配了新的URL。即页面被永久性转移走,在更换域名时起到很重要的作用。
302:与301类似,但是不是永久性转移而是临时性转移,如果用户把URL保存为书签,不会像301那样去更新书签。
303:和上面302的有相同的功能,但303明确表示客户端应该采用GET方法获取资源,比如用户使用post方法进行请求,此时返回303302更合适。
304:表示客户端发送附带条件请求时,服务端允许请求资源,但是未满足条件,响应报文中不包含任何响应主体部分。如:当发出一个GET请求时候,服务器会判断页面是否在服务器端更新了,如果没有更新,返回304状态码。

307:与302类似。


tip:当301、302、303响应状态码返回时,几乎所有的浏览器都会把POST改为GET,并删除请求报文中的主体,之后请求会自动再次发送。

301、302是禁止将post改为get的,但是使用的时候都会这么做。

见下:
这个回答其实在wikipedia上面有HTTP状态码的详细解释,我这里大概总结一下:
1. 对于301302location中包含的重定向url,如果请求method不是GET或者HEAD,那么浏览器是禁止自动重定向的,除非得到用户的确认,因为POSTPUT等请求是非冥等的(也就是再次请求时服务器的资源可能已经发生了变化)。
2. 虽然rfc明确了上述的规定,但是很多的浏览器不遵守这条规定,无论原来的请求方法是什么都会自动用GET方法重定向到location指定的url。就是说现存的很多浏览器在遇到POST请求返回301、302状态码的时候自动用GET请求location中的url,无需用户确认。
3. HTTP 1.1中新增了303、307状态码,用来明确服务器期待客户端进行何种反应。
4. 303状态码其实就是上面301、302状态码的”不合法”动作,指示客户端可以自动用GET方法重定向请求location中的url,无需用户确认。也就是把前面301302状态码的处理动作合法化了。
5. 307状态码就是301302原本需要遵守的规定,除GETHEAD方法外,其他的请求方法必须等客户确认才能跳转。
6. 303307其实就是把原来301302合法的处理动作给合法化,因为发现大家都不太遵守,所以干脆就增加一条规定。

 4**:错误发生在客户端
400:表示请求报文中存在语法错误,当发生错误时,需修改请求的内容后再次发送请求。
401:发送的请求没有通过HTTP认证。
403:访问的资源被服务器拒绝 如给访问的文件加上权限,访问时就会返回403
404:没有找到资源。也可以用服务器拒绝请求,但是不想说明理由时。

5**:服务器错误
500:执行请求时发生了错误,或者是web应用存在BUG
503:表示服务器暂时处于超负载或正在停机维护,无法处理请求,如果知道解除限制的时间,最好写入RetryAfter首部字段再返回给客户端,否则客户端以处理500响应的方式进行处理


  • 请求报文中要在Host首部内完整指定主机名或域名的URL:
单台虚拟主机模拟多个域名,即在物理层面只有一台服务器,但是只要使用虚拟主机的功能,则可以假象已具备多台服务器。但是域名通过DNS解析之后都是以IP地址进行访问,所以为了区分,必须在Host首部内完整指定主机名或域名的URL。


  • http的缺点以及解决方案:
1、通信使用明文(不加密),内容可能被窃听
2、不验证通信方的身份,可能遭到伪装,无意义的请求也会照单全收,DOS攻击
3、无法证明报文的完整性,所以有可能遭篡改

关于第一条中HTTP本身不会加密,所以在互联网中的任何一个节点都可能遭到窃听,比如我们使用抓包工具就可以窃听。为了解决这种情况,可以加密处理防止被窃听。根据加密的对象可以将加密分为通信加密和内容加密。

通信加密:

通过和SSL(secure socket layer)安全套接层,或TLS(transport layer security)安全层传输协议的组合使用,加密HTTP的通信内容。建立安全通信之后,就可以在这个线路上进行通信了,与SSL组合使用的HTTP被称为HTTPS(超文本传输安全协议)

内容加密:
即把报文里所含的内容进行加密,要求客户端和服务端同时具有加密和解密的过程,有被篡改的风险。

针对第二条中无法验证通信方的身份,可以通过SSL去查明对方的证书,SSL不仅提供加密处理,还可以使用证书去确定身份,证书由第三方发布,伪造成本高。

https :通信加密+证书+完整性保护

  •  HTTPS工作的过程:
SSL采用一种公开密钥加密的加密处理方式。

共享密钥加密的困境:加密和解密用同一个密钥的方式称为共享密钥加密。但是这种方式,在互联网上发送密钥,可能会被窃取,那么加密就没了意义。
而公开密钥加密使用两种密钥。发送方使用公开密钥进行加密,接收方使用私有密钥进行解密。
而HTTPs采用混合加密机制,因为公开密钥加密方式处理速度更慢,所以,HTTPs在交换密钥环节使用公开密钥加密方式,之后建立通信交换报文阶段则使用共享密钥加密方式。

总结:共享密钥只需要通过一种密钥完成加密解密,所以发送方加密之后还要把密文与密钥一起发送过来,在通信路线上会被窃取。

而公开密钥加密方式,会先获取接收方的公开密钥,根据公开密钥进行加密,然后把密文发送过来,接收根据自己的私钥进行解密。

但是还是有一个问题:怎么确保获取的公开密钥即是合法的接收方发送的,因为线路会被劫持,替代接收方发送公开密钥。

所以依托可信赖的第三方机构颁布证明公开密钥正确性的证书,即由服务器运营人员向机构申请认证,机构审核之后,然后给这个公开密钥分配个数字签名,放入证书绑定在一起。接收方例如服务器会把这个公钥证书发给发送方如客户端,发送方接收到证书后可去跟机构验证这个证书上的数字签名。在与认证机构进行通信的时候,对数据进行加密的过程也需要得到认证机构的公开密钥,而公开密钥如何安全的转接给接收方是个困难的事情,所以很多的浏览器会在内部植入常用的认证机关的公开密钥。所以不用通过线路获取了,直接根据公开密钥进行加密即可。

SSL通信部分消耗网络资源,又因为要对通信进行处理,所以时间上又延长了。
所以一般是在非敏感信息使用HTTP通信,只有包含个人信息等敏感数据时才利用HTTPS加密通信。


  • HTTP的瓶颈:
在大型的社交网站上,很短的时间内,就会有大量的信息更新,如果想要实时的显示这些更新到客户端,就要频繁的从客户端到服务器端进行确认,如果服务器上没有内容更新,就会产生徒劳的通信。使用AJax会缓解如上的问题,利用JS和DOM达到局部WEB页面替换加载的异步通信,由于只更新一部分页面,所以通信中传输的数据量减少。但是AJAX还是需要大量的请求,只是每次请求传送的数据量变小了,但这本身并没有解决HTTP协议本身存在的问题。

Comet的解决办法,一般情况下,服务端收到请求,处理完毕后会立即返回响应,为了实现上述功能,Comet会将响应置于挂起状态,当服务器端有内容更新的时候再响应。虽然可以做到实时更新,但是为了保留响应会消耗更多的资源,另外Comet仍未解决HTTP协议本身存在的问题。

利用ajax和comet技术进行通信可以提升web浏览速度,但问题在于若使用http协议就无法彻底解决瓶颈问题。websocket技术正是为了解决这些问题而实现的一套新协议及API,一旦web服务器与客户端之间建立了websocket通信连接,之后所有的通信都依靠这个专用协议进行。通信过程中可以互相发送JSON,XML,HTML或图片等任意格式的数据。

由于建立在HTTP基础上的协议,所以发起方仍然是客户端,且一旦确定了websocket连接,不论服务器还是客户端,任意一方都可直接向对方发送报文。

  • CGI(Common Gateway  Interface)通用网关接口:

指web服务器在接收到客服端发送过来的请求后转发给程序的一组机制。在CGI的作用下, 程序会对请求内容做处相应的动作,比如创建HTML等动态内容,使用CGI的程序叫做CGI程序,通常是:perl,php,ruby,c等编程语言编写而成。

之前提及的 CGI,由于每次接到请求,程序都要跟着启动一次。因此一旦访问量过大,Web 服务器要承担相当大的负载。而 Servlet 运行在与 Web 服务器相同的进程中,因此受到的负载较小 。Servlet 的运行环境叫做 Web 容器或 Servlet 容器。

Servlet 常驻内存,因此在每次请求时,可启动相对进程级别更为轻量的Servlet,程序的执行效率从而变得更高。作为解决 CGI 问题的对抗技术随 Java 一起得到了普及。说对抗的原因在于,这个方向上已存在用 Perl 编写的 CGI,实现在 ApacheHTTP Server上内置 modphp 模块后可运行 PHP 程序、微软主导的 ASP等技术。

随着 CGI 的普及,每次请求都要启动新 CGI 程序的 CGI 运行机制逐渐变成了性能瓶颈,所以之后 Servlet 和 mod_perl 等可直接在 Web 服务器上运行的程序才得以开发、普及。