本文为原创作品。欢迎转载,转载请注明出处:林东洲的博客 | Lindz Blog。
作为一个 Weber,掌握必要的 HTTP/TCP 等协议十分必要。自从上学期学习了计算机网络,对于网络这一块有了一定的了解,再加上网上学习到的知识,通过本文记录自己对 HTTP 协议的理解。
首先让我们从一个问题入手,当我们在浏览器中输入 http://www.baidu.com 访问百度的时候浏览器做了哪些事情。(这里以 Chrome 浏览器为例)
- 首先 Chrome 搜索自身的 DNS 缓存。
如果 DNS 缓存中找到百度的IP地址,就跳过了接下来查找IP地址步骤,直接访问该 IP 地址。
- 搜索操作系统自身的 DNS 缓存。(浏览器没有找到缓存或者缓存已经失效)
- 读取本身的 HOST 文件。(如果前1.2步骤都没有找到)
- 浏览器向宽带运营商服务器或者域名服务器发起一个 DNS 解析请求。
服务器先查看本身的缓存,若找到则返回百度首页的 IP 地址,若没有找到,则服务器就发起一个迭代 DNS 解析的请求,如下图:
从图中我们可以看出域名服务器先向根域名服务器发起DNS解析请求,接着迭代发起请求,直到找到所搜索的百度首页的 IP 地址,并将它返回给浏览器。
- 拿到 IP 地址后,浏览器就向该 IP 所在的服务器发起 TCP 连接(即三次握手)。
- 连接建立起来之后,浏览器就可以向服务器发起 HTTP 请求了。
这里比如访问百度首页,就向服务器发起 HTTP 中的 GET 请求。
- 服务器接受到这个请求后,根据路径参数,经过后台一些处理之后,把处理后的结果返回给浏览器,如果是百度首页,就可以把完整的 HTML 页面代码返回给浏览器。
- 浏览器拿到了百度首页的完整 HTML 页面代码,内核和 JS 引擎就会解析和渲染这个页面,里面的 JS,CSS,图片等静态资源也通过一个个 HTTP 请求进行加载。
- 浏览器根据拿到的资源对页面进行渲染,最终把完整的页面呈现给用户。
- 如果浏览器没有后续的请求,那么就会跟服务器端发起 TCP 断开(即四次挥手)。
至此,整个访问过程就结束了,这里只是简单的概括,实际情况远比这些复杂。
HTTP 概念
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。
举个可能不太恰当的例子,TCP 就好比端与端之间沟通的桥梁,而 HTTP 就像是一个搬运工,通过这个桥梁搬运资源。
HTTP 特性:
- HTTP 构建于 TCP/IP 协议之上,默认端口号是 80
- HTTP 是无连接无状态的
HTTP 分为两个部分:请求和响应。
HTTP 内容
HTTP 请求:
HTTP 定义了在与服务器交互的不同方式,最常用的方法有 4 种,分别是 GET,POST,PUT, DELETE。URL 全称为资源描述符,可以这么认为:一个 URL 地址,对应着一个网络上的资源,而 HTTP 中的 GET,POST,PUT,DELETE 就对应着对这个资源的查询,修改,增添,删除4个操作。
HTTP 请求由 3 个部分构成,分别是:状态行,请求头(Request Header),请求正文。
状态行由协议,状态描述等构成,各元素之间以空格分隔。
请求头提供一些参数比如:Cookie,用户代理信息,主机名等等。
请求正文就放一些发送的数据,一般 GET 请求会将参数放在 URL 中,也就是在请求头中而请求正文一般为空,而 POST 请求将参数放在请求正文中。
- GET 一般用于信息获取,比如刚才我们浏览百度首页,其使用的就是GET方法。
GET请求报文实例:
GET 请求一般不会产生副作用,它仅仅只是获取资源信息,就像数据库查询一样,不会修改、增加数据,不会影响资源的状态,并且对同一个 URL 的多次GET请求应该返回相同的结果。
- POST 请求表示可能会修改服务器上的资源。
注:
- GET 和 POST 请求参数位置不同,从上面两个请求报文可以看出,GET 请求对应的参数放在 URL 中,而 POST 请求对应的参数放在 HTTP 请求主体中。
- GET 可提交的数据量受 URL 长度的限制,而 POST 的数据量则没有限制。
- 处于安全考虑,在一些涉及安全的请求比如:登录请求需要用 POST 提交表单,而GET 请求一般用来获取静态资源。
HTTP 响应:
HTTP 响应是服务器在客户端发送 HTTP 请求后经过一些处理而做出的响应,HTTP 响应和 HTTP 请求相似,也是由三个部分构成。分别是:状态行,响应头(Response Header),响应正文。
HTTP 响应中包含一个状态码,用来表示服务器对客户端响应的结果。
状态码一般由3位构成:
- 1xx : 表示请求已经接受了,继续处理。
- 2xx : 表示请求已经处理掉了。
- 3xx : 重定向。
- 4xx : 一般表示客户端有错误,请求无法实现。
- 5xx : 一般为服务器端的错误。
比较常用的状态码:
- 200 OK 客户端请求成功。
- 301 Moved Permanently 请求永久重定向。
- 302 Moved Temporarily 请求临时重定向。
- 304 Not Modified 文件未修改,可以直接使用缓存的文件。
- 400 Bad Request 由于客户端请求有语法错误,不能被服务器所理解。
- 401 Unauthonzed 请求未经授权,无法访问。
- 403 Forbidden 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因。
- 404 Not Found 请求的资源不存在,比如输入了错误的URL。
- 500 Internal Server Error 服务器发生不可预期的错误,导致无法完成客户端的请求。
- 503 Service Unavailable 服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常。
下面是一个 HTTP 响应的例子:
知道了 HTTP 请求和响应后,一个完整的流程一般是这样的:
通常,由 HTTP 客户端发起一个请求,建立一个到服务器指定端口(默认是 80 端口)的 TCP 连接。HTTP 服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行,比如"HTTP/1.1 200 OK",和(响应的)消息,消息的消息体可能是请求的文件、错误消息、或者其它一些信息。
HTTP 使用 TCP 而不是 UDP 的原因在于(打开)一个网页必须传送很多数据,而 TCP 协议提供传输控制,按顺序组织数据,和错误纠正。
其它
了解以上那些概念后,已经对 HTTP 协议有了大致的了解了。下面介绍一些 HTTP 实现中具体内容。
会话追踪
- 会话:客户端向服务器端发起请求到服务端响应客户端请求的全过程。
- 会话跟踪:会话追踪指的是服务器对用户响应的监视。
为什么需要会话跟踪:
浏览器与服务器之间的通信是通过 HTTP 协议进行通信的,而 HTTP 协议是”无状态”的协议,它不能保存客户的信息,即一次响应完成之后连接就断开了,下一次的请求需要重新连接,这样就需要判断是否是同一个用户,所以才有会话跟踪技术来实现这种要求。
会话跟踪常用方法:
- URL 重写:URL 重写技术就是在 URL 结尾添加一个附加数据以标识该会话,把会话 ID 通过 URL 的信息传递过去,以便在服务器进行识别不同的用户。
- 隐藏表单域:将会话ID添加到HTML表单元素中提交到服务器,此表单元素并不在客户端显示。
- Cookie:Cookie 是 Web 服务器发送给客户端的一小段信息,客户端请求时可以读取该信息发送给服务器端,进而进行用户的识别,对于客户端的每次请求,服务器都会将 Cookie 发送到客户端,客户端保存下来,以便下次使用。
客户端可以采用两种方式来保存这个Cookie对象,一种方式是保存在客户端内存中,称为临时Cookie,浏览器关闭后这个Cookie对象将消失。
另外一种方式是保存在客户机的磁盘上,称为永久Cookie。以后客户端只要访问该网站,就会将这个Cookie再次发送到服务器上,前提是这个Cookie在有效期内,这样就实现了对客户的跟踪。
Cookie 是可以被禁止的,当你打开 Chrome,在设置里面关闭 Cookie,那么你将再也无法登录淘宝页面。
- Session:在服务器端会创建一个 session 对象,产生一个 sessionID 来标识这个 session 对象,然后将这个 sessionID 放入到 Cookie 中发送到客户端,下一次访问时,sessionID 会发送到服务器,在服务器端进行识别不同的用户。
每一个用户都有一个不同的 session,各个用户之间是不能共享的,是每个用户所独享的,在 session 中可以存放信息。
Session的实现依赖于Cookie,如果Cookie被禁用,那么session也将失效。
条件 GET:
HTTP 条件 GET 是 HTTP 协议为了减少不必要的带宽浪费,提出的一种方案。
- HTTP 条件 GET 使用时机:客户端之前已经访问过某网站,并打算再次访问该站点。
- HTTP 条件 GET 使用的方法:客户端向服务器发送一个包询问是否在上一次访问网站的时间后是否更改了页面,如果服务器没有更新,显然不需要把整个网页传给客户端,客户端只要使用本地缓存即可,如果服务器对照客户端给出的时间已经更新了客户端请求的网页,则发送这个更新了的网页给用户。
下面是一个具体的发送接受报文示例:
客户端发送请求:
第一次请求时,服务器端返回请求数据,之后的请求,服务器根据请求中的 If-Modified-Since 字段判断响应文件没有更新,如果没有更新,服务器返回一个 304 Not Modified响应,告诉浏览器请求的资源在浏览器上没有更新,可以使用已缓存的上次获取的文件。
如果服务器端资源已经更新的话,就返回正常的响应。
持久连接:
我们知道 HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用 Keep-Alive 模式(又称持久连接、连接重用)时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。
在 HTTP 1.0 版本中,并没有官方的标准来规定 Keep-Alive 如何工作,因此实际上它是被附加到 HTTP 1.0协议上,如果客户端浏览器支持 Keep-Alive ,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有 Connection: Keep-Alive 的请求时,它也会在响应头中添加一个同样的字段来使用 Keep-Alive 。这样一来,客户端和服务器之间的HTTP连接就会被保持,不会断开(超过 Keep-Alive 规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。
在 HTTP 1.1 版本中,默认情况下所有连接都被保持,如果加入 "Connection: close" 才关闭。目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起 Keep-Alive 的连接请求了,所以是否能完成一个完整的 Keep-Alive 连接就看服务器设置情况。
由于 HTTP 1.0 没有官方的 Keep-Alive 规范,并且也已经基本被淘汰,以下讨论均是针对 HTTP 1.1 标准中的 Keep-Alive 展开的。
小结:
- 会话跟踪和条件 GET 是有不同的,它们分别运用于不同的场景。会话跟踪是服务器用来表示标识区分不同的用户,而有条件 GET 则用户多次访问相同的页面,用来减少带宽的浪费,两者有本质上的区别。
- HTTP Keep-Alive 简单说就是保持当前的TCP连接,避免了重新建立连接。
- HTTP 是一个无状态无连接的协议,那么这是不是与 Keep-Alive 冲突?
个人认为:Keep-Alive 与无连接的特性冲突,而对于无状态的特性两者并无矛盾,HTTP 无状态无连接是在 1.0 版本中就规定的,而 Keep-Alive 则是在 1.1 版本中才被添加入规范。
无连接的意思是限制每个连接只有一个请求的意思,在服务器处理完客户的请求,并收到客户的反应,即断开。通过这种方式可以节省传输时间。
Keep-Alive 确实破坏了这一特性,而无状态协议则意味着每个请求都是独立的,互不干扰的,互相没有记忆的。所以才需要有会话跟踪这种机制来识别用户。
参考链接: