一, HTTP 概况
20 世纪 90 年代初期, 一个主要的新兴应用即万维网 (World Wide web) 登上了舞台. Web 是一个引起公众注意的因特网应用. Web 的应用层协议是超文本传输协议(HTTP), 它是 Web 的核心. HTTP 由两个程序实现: 一个客户程序和一个服务器程序. 客户程序和服务器程序运行在不同的端系统中, 通过交换 HTTP 报文进行会话. HTTP 会话定义了这些报文的结构以及客户和服务器进行报文交换的方式.
Web 页面 (也叫文档) 是由对象组成的. 一个对象只是一个文件, 诸如一个 html 文件, 一个 JPEG 图形, 一个 Java 小程序或一个视频片段这样的文件, 且他们可通过一个 URL 地址寻址. 多数 Web 页面含有一个 HTML 基本文件以及几个引用对象. 例如, 如果一个 Web 页面包含 HTML 基本文件和 5 个 JPEG 图形, 那么这个 Web 页面 6 个对象: 一个 HTML 基本文件加 5 个图形. HTML 基本文件通过对象的 URL 地址引用页面中的其他对象. 每个 URL 地址由两部分组成: 存放对象的服务器主机名和对象的路径名. Web 浏览器实现了 HTTP 的客户端, Web 服务器实现了 HTTP 的服务器端, 它用于存储 Web 对象, 每个对象由 URL 寻址.
HTTP 定义了 Web 客户向 Web 服务器请求 Web 页面的方式, 以及服务器向客户传送 Web 页面的方式, 其基本思想就是当用户请求一个 Web 页面 (如点击一个超链接) 时, 浏览器向服务器发出对该页面中所包含对象的 HTTP 请求报文, 服务器接收到请求并用包含这些对象的 HTTP 响应报文进行响应.
HTTP 使用 TCP 作为它的支撑运输协议(而不是在 UDP 上运行).HTTP 客户首先发起一个与服务器的 TCP 连接. 一旦连接建立, 该浏览器和服务器进程就可以通过套接字接口访问 TCP. 客户向它的套接字接口发送 HTTP 请求报文并从它的套接字接口接收 HTTP 响应报文. 类似的, 服务器从它的套接字接口接收 HTTP 请求报文和向它的套接字接口发送 HTTP 响应报文. 一旦客户向他的套接字接口发送了一个请求报文, 该报文就脱离了客户控制并进入 TCP 的控制. TCP 为 HTTP 提供可靠数据传输服务. 这意味着, 一个客户进程发出的每个 HTTP 请求报文最终能完整地到达服务器; 类似的, 服务器进程发出的每个 HTTP 响应报文最终能完整地到达客户.
注意到下列现象很重要: 服务器向客户发送被请求的文件, 而不存储任何关于该客户的状态信息. 假如某个特定的客户在短短的几秒钟内两次请求同一个对象, 服务器并不会因为刚刚为该客户提供了该对象就不再做出反应, 而是重新发送该对象, 就像服务器已经完全忘记不久之前所做过的事一样. 因为 HTTP 服务器并不保存关于客户的任何信息, 所以我们说 HTTP 是一个无状态协议.
二, 非持续连接和持续连接
在许多因特网应用程序中, 客户和服务器在一个相当长的时间范围内通信, 其中客户发出一系列请求并且服务器对每个请求进行响应. 依据应用程序以及该应用程序的使用方式, 这一系列请求可以以规则的间隔周期性的或者间断性的一个接一个发出. 当这种客户 - 服务器的交互是经 TCP 进行的, 应用程序的研制者就要做一个重要决定, 即每个请求 / 响应对是经一个单独的 TCP 连接发送, 还是所有的请求及其相应经相同的 TCP 连接发送呢? 采用前一种方法, 该应用程序被称为使用非持续连接; 采用后一种方法, 该应用程序被称为使用持续连接. 如 HTTP 既能够使用非持续连接, 也能够使用持续连接. 尽管 HTTP 在默认方式下使用持续连接, HTTP 客户和服务器也能配置成非持续连接.
1. 采用非持续连接的 HTTP
我们看看在非持续连接情况下, 从服务器向客户传送一个 Web 页面的步骤. 假设该页面含有一个 HTML 基本文件和 10 个 JPEG 图形, 并且这 11 个对象位于同一台服务器上. 该 HTML 文件的 URL 为: http://www.someSchool.edu/someDepartment/home.index. 我们看看发生了什么情况:
HTTP 客户进程在端口号 80 发起一个到服务器 www.someSchool.edu 的 TCP 连接, 该端口号是 HTTP 的默认端口. 在客户和服务器上分别有一个套接字与该连接相关联.
HTTP 客户经它的套接字向该服务器发送一个 HTTP 请求报文. 请求报文中包含了路径名 / someDepartment/home.index.
HTTP 服务器进程经它的套接字接收该请求报文, 从其存储器 (RAM 或磁盘) 中检索出对象 http://www.someSchool.edu/someDepartment/home.index, 在一个 HTTP 响应报文中封装对象, 并通过其套接字向客户发送响应报文.
HTTP 服务器进程通知 TCP 断开该 TCP 连接.(但是直到 TCP 确认客户已经完整的收到响应报文为止, 它才会实际中断连接.
HTTP 客户接收响应报文, TCP 连接关闭. 该报文指出封装的对象是一个 HTML 文件, 客户从响应报文中提取出该文件, 检查该 HTML 文件, 得到对 10 个 JPEG 图形的引用.
对每个引用的 JPEG 图形对象重复前 4 个步骤.
上面的步骤举例说明了非持续连接的使用, 其中每个 TCP 连接在服务器发送一个对象后关闭, 即该连接并不为其他的对象而持续下来. 值得注意的是每个 TCP 来接只传输一个请求报文和响应报文.
在上面描述的步骤中, 我们有意没有明确客户获得这 10 个 JPEG 图形对象是使用 10 个串行的 TCP 连接, 还是某些 JPEG 对象使用了一些并行的 TCP 连接. 事实上, 用户能配置现代浏览器以控制并行度. 在默认方式下, 大部分浏览器打开 5~10 个并行的 TCP 连接, 而每条连接处理一个请求响应事务. 如果用户愿意, 最大并行连接数可以设置为 1, 这样 10 条连接就会串行建立.
我们来简单估算一下从客户请求 HTML 基本文件起到该客户收到整个文件止所花费的时间. 为此, 我们给出往返时间 (Round-Trip Time,RTT) 的定义, 该时间是指一个短分组从客户到服务器然后再返回客户所花费的时间. RTT 包括分组传播时延, 分组在中间路由器和交换机上的排队时延以及分组处理时延. 现在考虑当用户点击超链接时会发生什么现象. 如图 2-7 所示, 这引起浏览器在它和 Web 服务器之间发起一个 TCP 连接; 这涉及一次 "三次握手" 过程. 即客户向服务器发送一个小 TCP 报文段, 服务器用一个小 TCP 报文段做出确认和响应, 最后, 客户向服务器返回确认. 三次握手中前两个部分所耗费的时间占用了一个 RTT. 完成了三次握手的前两个部分后, 客户结合三次握手的第三部分 (确认) 向该 TCP 连接发送一个 HTTP 请求报文. 一旦该请求报文到达服务器, 服务器就在该 TCP 连接上发送 HTML 文件. 该 HTTP 请求 / 响应用去了另一个 RTT. 因此, 粗略地将, 总的响应时间就是两个 RTT 加上服务器传输 HTML 文件的时间.
2. 采用持续连接的 HTTP
非持续连接有一些缺点. 首先, 必须为每一个请求的对象建立和维护一个全新的连接. 对于每个这样的连接, 在客户和服务器中都要分配 TCP 的缓冲区和保持 TCP 变量, 这给 Web 服务器带来了严重的负担, 因为一台 Web 服务器可能同时服务于数以百计不同的客户的请求. 第二, 就像我们刚描述的那样, 每一个对象经受两倍 RTT 的交付时延, 即一个 RTT 用于创建 TCP, 另一个 RTT 用于请求和接收一个对象.
在采用持续连接的情况下, 服务器在发送响应后保持该 TCP 连接打开. 在相同的客户与服务器之间的后续请求和响应报文能够通过相同的连接进行传送. 特别是, 一个完整的 Web 页面 (上例中的 HTML 基本文件加上 10 个图形) 可以用单个持续 TCP 连接进行传送. 更有甚者, 位于同一台服务器的多个 Web 页面在从该服务器发送给同一个客户时, 可以在单个持续 TCP 连接上进行. 可以一个接一个地发出对对象的这些请求, 而不必等待对未决请求 (流水线) 的回答. 一般来说, 如果一条连接经过一定的时间间隔 (一个可配置的超时间隔) 仍未被使用, HTTP 服务器就关闭该连接. HTTP 的默认模式是使用带流水线的持续连接.
三, HTTP 报文格式
HTTP 报文有两种: 请求报文和响应报文.
1.HTTP 请求报文
下面提供了一个典型的 HTTP 请求报文:
- GET /somedir/page.HTML HTTP/1.1
- Host: www.someschool.edu
- Connection: close
- User-agent: Mozilla/5.0
- Accept-language: fr
通过仔细观察这个简单的请求报文, 我们就能知道很多东西. 首先, 我们看到该报文是用普通的 ASCII 文本书写的, 我们看到该报文由 5 行组成, 每行由一个回车和换行符结束. 最后一行后再附加一个回车换行符. 一个请求报文能够具有更多的行或者至少为一行. 请求行的方法字段可以取几种不同的值, 包括 GET,POST,HEAD,PUT 和 DELETE. 当浏览器请求一个对象时, 使用 GET 方法, 在 URL 字段带有请求对象的标识, 在本例中, 该浏览器正在请求对象 / somedir/page.HTML. 其版本字段是自解释的; 在本例中, 浏览器实现的是 HTTP/1.1 版本. 现在我们看看本例的首部行. 首部行 Host: www.someschool.edu 指明了对象所在的主机. 你也许认为该首部行是不必要的, 因为在该主机中已经有一条 TCP 连接存在了, 但是, 该首部行提供的信息是 Web 代理高速缓存所要求的. 通过包含 Connection: close 首部行, 该浏览器告诉服务器不希望麻烦地使用持续连接, 它要求服务器在发送完被请求的对象后就关闭这条连接. User-agent: 首部行用来指明用户代理, 即向服务器发送请求的浏览器类型. 这里浏览器类型是 Mozilla/5.0, 即 Firefox 浏览器. 这个首部行是有用的, 因为服务器可以有效地为不同类型的用户代理实际发送相同对象的不同版本.(每个版本都由相同的 URL 寻址.)最后, Accept-language: 首部行表示用户想得到该对象的法语版本. 如果服务器中没有这样的对象的话, 服务器应当发送它的默认版本.
接下来看看如图 2-8 所示的一个请求报文的通用格式. 你可能注意到了在首部行 (和附加的回车和换行) 后有一个 "实体主体". 使用 GET 方法是实体主体为空, 而使用 POST 方法时才使用该实体主体. 当用户提交表单时, HTTP 客户常常使用 POST 方法, 例如当用户向搜索引擎提供搜索关键词时. 使用 POST 报文时, 用户仍可以向服务器请求一个 Web 页面, 但 Web 页面的特定内容依赖于用户在表单字段中输入的内容. 如果方法字段的值为 POST 时, 则实体主体中包含的就是用户在表单字段中的输入值.
当然, 如果不提 "用表单生成的请求报文不是必须使用 POST 方法" 这一点, 那将是失职. HTML 表单经常使用 GET 方法, 并在 (表单字段中) 所请求的 URL 中包括输入的数据. 例如, 一个表单使用 GET 方法, 它有两个字段, 分别填写的是 "monkeys" 和 "bananas", 这样, 该 URL 结构为 www.somesite.com/animalsearch? monkeys&bananas.
HEAD 方法类似 GET 方法. 当服务器收到使用 HEAD 方法的请求时, 将会用一个 HTTP 报文进行响应, 但是并不返回请求对象. 应用程序开发者常用 HEAD 方法进行调试跟踪. PUT 方法常与 Web 发行工具联合使用, 它允许用户上传对象到指定的 Web 服务器上指定的路径(目录).PUT 也被那些需要向 Web 服务器上传对象的应用程序使用. DELETE 方法允许用户或者应用程序删除 Web 服务器上的对象.
2.HTTP 响应报文
下面我们提供了一条典型的 HTTP 响应报文. 该响应报文可以是对刚刚讨论的例子中请求报文的响应.
- HTTP/1.1 200 OK
- Connection: close
- Date: Tue, 09 Aug 2011 15:44:04 GMT
- Server: Apache/2.2.3 (CentOS)
- Last-Modified: Tue, 09 Aug 2011 15:11:03 GMT
- Content-Length: 6821
- Content-Type: text/HTML
- (data data data data data ...)
我们仔细看这个响应报文. 实体主体部分是报文的主要部分, 即它包含了所请求的对象本身 (表示为 data data data data data ...). 我们现在来看看首部行. 服务器用 Connection:close 首部行告诉客户, 发送完报文后将关闭该 TCP 连接. Date: 首部行指示服务器产生并发送该响应报文的日期和时间. 值得一提的是, 这个时间不是指对象创建或者最后修改的时间; 而是服务器从它的文件系统中检索到该对象, 插入到响应报文, 并发送响应报文的时间. Server: 首部行指示该报文是由一台 Apache Web 服务器产生的, 它类似于 HTTP 请求报文中的 User-agent: 首部行, Last-Modified: 首部行指示了对象创建或者最后修改的日期和时间. Last-Modified: 首部行对极可能在本地客户也可能在网络缓存服务器(代理服务器) 上的对象缓存来说非常重要. Content-Length: 首部行知识了被发送对象中的字节数. Content-Type: 首部行指示了实体主体中的对象是 HTML 文本.(该对象类型应该正式地由 Content-Type: 首部行而不是用文件扩展名来指示.)
看过一个例子后, 我们再来查看响应报文的通用格式(如图 2-9 所示). 我们补充说明一下状态码和它们对应的短语. 状态码及其相应的短语指示了请求的结果. 一些常见的状态码和相关的短语包括:
200OK: 请求成功, 信息在返回的响应报文中.
301 Moved Permanently: 请求的对象已经被永久转移了, 新的 URL 定义在响应报文的 Location: 首部行中.** 客户软件将自动获取新的 URL.
400 Bad Request: 一个通用差错代码, 指示该请求不能被服务器理解.
404 Not Found: 被请求的文档不在服务器上.
505 HTTP Version Not Supported: 服务器不支持请求报文使用的 HTTP 协议版本.
你想看一下真正的 HTTP 响应报文吗? 很容易做到. 首先用 Telnet 登录到你喜欢的 Web 服务器上, 接下来输入一个只有一行的请求报文去请求放在该服务器上的某些对象.
在 Linux 终端输入完 telnet www.baidu.com80 后, 会是下面这种情况:
然后按下 ctrl + ]呼出 telnet 命令行出现下面这种情况:
先按下回车键, 再输入 HTTP 请求, 最终得到 HTTP 响应如下:
在 telnet 命令行上输入 quit 退出 telnet, 如下图:
来源: https://www.cnblogs.com/Gland/p/HTTP.html