一篇文章带你认识HTTP协议

344 阅读9分钟

HTTP概述

HTTP协议是什么

HTTP协议(HyperText Transfer Protocol),又称超文本传输协议,是网络模型中应用层的一个协议,通常被用来在一个服务端程序和客户端程序间传输HTTP报文(下文会提到),以达到传输HTML文件、图片、视频的效果。 HTTP使用TCP作为它的支撑运输协议,这句话的意思是,HTTP协议本身并没有传输的部分,它仅仅是把需要传输的内容封装为HTTP数据报,然后交给TCP协议去传输,由于TCP协议是一个可靠的协议,这也就意味着在一般情况下,一个客户端进程发出的HTTP请求总能完整的到达服务器,反过来,我们也可以认为在一般情况下服务器返回的HTTP报文也总能完整的到达发出响应请求的客户端。

HTTP是一个无状态协议

HTTP是一个无状态协议,这句话的意思是说服务端程序在向客户端程序发送文件时并不会存储任何客户端的状态信息,这是有悖于我们的常识的,因为现在我们在浏览网页时大部分的信息都与账户信息有关(比如登陆电商网站去查看自己的购物车数据),但是在互联网最初的时候大部分网页都是静态的,并不需要做任何与用户信息相关的交互,但是随着互联网的发展,这种与用户信息之间交互的需求越来越多,开发人员就想到了在HTTP报文中添加一个叫做cookie的东西来解决这一问题,但是我们仍然认为HTTP协议是一个无状态协议,因为通过cookie来返回相应的数据是后端程序的任务,与HTTP协议本身无关。

HTTP的非持续连接和持续连接

非持续连接

非持续性链接即每一个连接只传输一个文件,比如我们访问百度首页时除了要获取百度首页的html文件,还要下载百度首页的logo,如果采用非持续连接,将会产生两个TCP传输通道,用来分别传输html文档和图片。 我们知道TCP在连接时有一个三次握手和四次挥手的过程,这个过程会消耗一定的时间,在默认情况下,一个浏览器能够打开5~10个并行的TCP连接,当请求的文件数大于该数目的时候,频繁的握手与挥手会导致时间上的损耗。同时频繁的创建与销毁TCP连接也会带来一些服务器性能上的损耗。

持续连接

持续连接即尽可能的复用客户端与服务端的TCP连接,意思就是在传输完当前的文件后不立即关闭该TCP连接,而是等待一段时间(可自由设置),如果在等待的时间内再次收到客户端的请求,则继续使用该TCP连接来进行传输。这样一来就解决了非持续连接带来的问题,但同时如果等待时间设置得不合理也会带来许多问题,应合理的设置等待的时间。

HTTP报文

请求报文

一个典型的HTTP请求报文结构如下图所示 请求报文是由一行请求行和若干行首部行(headers)以及实体体(entity body,也有别的叫法叫请求体)组成,其中只有请求行是必须的,其他两个在某些情况下并不一定会存在。 在linux下我们可以使用crul命令来查看HTTP的请求报文和响应报文 我们使用curl -v http://www.baidu.com/命令来查看当访问百度时的HTTP请求报文,部分返回结果如下所示

请求行

请求行有三个字段,分别是方法字段、URL字段和HTTP版本字段

方法字段

HTTP的方法包括GET、POST、HEAD、PUT和DELETE五种。 绝大多数的HTTP请求都使用的是GET方法,当我们在浏览器中输入一个网址得到相应的网页时,也是使用的GET方法。 POST方法用来向服务器提交一些信息,比如说登陆时的用户名和密码,这些信息被保存在上面提到的实体体中,然后被传输到服务器。 HEAD方法与GET方法类似,它们的区别仅在于HEAD方法不会返回GET方法请求的对象(比如HTML文件),仅仅返回请求行和首部行。 PUT方法用来向服务器端上传文件。 DELETE用来删除服务器上的文件。

URL字段

即我们请求的文件在服务器中的位置,因为我们这里访问的是百度的首页,默认的就是根路径,所以这里的URL字段为“/”,如果我们使用curl -v http://www.baidu.com/index.php,URL字段的值就会是"/index.php"

HTTP版本

传输使用的HTTP协议的版本,在这个示例中我们使用的是HTTP1.1

首部行

首部行中存储着一些信息,多条首部行之间用回车符和换行符隔开,常见的如Host,用来表示请求访问的主机名、User-Agent,用来表示发出客户端请求的浏览器版本,由于我这里使用的是curl命令所以显示的是curl

实体体

在上文中已经提到过,存储请求的信息,但并不是每一个HTTP请求报文都有实体体

响应报文

一个典型的HTTP响应报文结构如下图所示 可以看出响应报文的结构与请求报文结构很相似,仅仅是把请求行更改为了状态行 同样我们使用curl -v http://www.baidu.com命令查看该响应报头

状态行

版本

与请求报文相同,代表HTTP的版本

状态码与短语

状态码与短语是一一对应的,它们结合起来共同反应了请求的结果,下面是一些常见的状态码与短语以及它们所代表的含义

  • 200 OK:请求成功,信息在返回的响应报文中
  • 301 Moved Permanently:请求的对象已经被永久转移了,但是并没有删除,该对象现在在服务器中的路径会被放在响应报文的Location:xxxxxxx首部行中,以便于客户端再次请求
  • 400 Bad Request:一个通用的差错代码,指该请求服务器不能理解
  • 404 Not Found:请求的内容在服务器上不存在
  • 505 HTTP Version Not Support:服务器不支持请求所用的HTTP协议版本

首部行

响应报文的首部行和请求报文的首部行作用基本一致,所以这里仅仅介绍几个常见的响应报文首部行的含义。

  • Connection:close/Keep-Alive 分别对应上面说到的该HTTP使用非持续连接还是持续连接
  • Date 服务器发送该响应报文的时间
  • Content-Length 响应报文中实体体的长度,单位是byte
  • Content-Type 响应报文返回的对象类型,可以看到这里返回的是一个html文档或者普通文档类型
  • Last-Modified 该文件在服务器上最后被修改的时间,这一条我们最后在说代理服务器的时候会再次说到
  • Server 响应的服务器的名称以及版本

代理服务器相关

如果所有的网络请求都直接向其初始服务器发送的话,会给初始服务器带来严重的负担,此时我们需要在客户端和服务端之间架设代理服务器来解决这一问题(如下图所示)。 它的工作原理是这样的,客户端发送的请求首先发送到代理服务器,如果代理服务器中存在客户端想要的内容,那么代理服务器就可以把该内容直接返回,不需要向原始服务器发送请求,从而减少了初始服务器的负担。以上的内容可能比较抽象,下边我们通过一个例子来分析。 假如我是第一次访问百度的首页,且代理服务器中没有保存相关的对象,那么整个流程的顺序是这样的

  1. 客户端向代理服务器发送请求表示自己要请求百度的主页
  2. 代理服务器先检查本地是否有百度的主页,检查后发现本地并没有,于是代理服务器向初始服务器发送请求向初始服务器索要相关的对象
  3. 初始服务器接受到请求把请求的对象返回给代理服务器
  4. 代理服务器接收到初始服务器返回的对象,在本地保存一份副本之后再将该对象返回给客户端
  5. 客户端收到代理服务器返回的数据,在浏览器上显示相应的界面

从此之后我再访问百度主页,整个流程就简单了很多,如下所示

  1. 客户端向代理服务器发送请求表示自己要请求百度的主页
  2. 代理服务器先检查本地是否有百度的主页,检查后发现本地有百度首页的文件,将该对象返回给客户端
  3. 客户端收到代理服务器返回的数据,在浏览器上显示相应的界面

但是这样就导致了一个问题,就是如果服务器上的文件在代理服务器保存之后进行了更改怎么办,这时就需要用到我们上面说到的Last-Modified首部行,当代理服务器接受到请求时,它向初始服务器发送一个请求去对比本地文件的Last-Modified与服务器上的Last-Modified是否一致,如果一致就直接将本地的对象返回,如果不一致就再次从初始服务器下载一份覆盖掉本地对象,再将该对象返回给客户端。