第二章 应用层(中)

目录

    • [2.2 Web和HTTP](#2.2 Web和HTTP)
      • [2.2.1 HTTP概况](#2.2.1 HTTP概况)
      • [2.2.2 非持续连接和持续连接](#2.2.2 非持续连接和持续连接)
        • [2.2.2.1 非持续链接](#2.2.2.1 非持续链接)
        • [2.2.2.2 持续连接](#2.2.2.2 持续连接)
      • [2.2.3 HTTP报文格式](#2.2.3 HTTP报文格式)
        • [2.2.3.1 请求行](#2.2.3.1 请求行)
        • [2.2.3.2 首部行](#2.2.3.2 首部行)
        • [2.2.3.3 实体体](#2.2.3.3 实体体)
        • [2.2.3.4 状态行](#2.2.3.4 状态行)
        • [2.2.3.5 响应首部行](#2.2.3.5 响应首部行)
        • [2.2.3.6 实体体](#2.2.3.6 实体体)
      • [2.2.4 用户与服务器的交互:cookie](#2.2.4 用户与服务器的交互:cookie)
      • [2.2.5 Web缓存](#2.2.5 Web缓存)
      • [2.2.6 条件GET方法](#2.2.6 条件GET方法)

2.2 Web和HTTP

2.2.1 HTTP概况

因特网上最重要、最常用的应用之一就是 Web

而支撑 Web 的应用层协议叫 HTTP

所以这一节主要干两件事:

  1. 先用几段话告诉你:Web 为啥这么重要、它有什么特点;
  2. 然后正式进入 HTTP:
    • Web 页面是什么;
    • URL 是什么;
    • 浏览器、Web 服务器分别干嘛;
    • HTTP 怎么在 TCP 上跑;
    • HTTP 是"无状态协议"是什么意思。

回顾一下历史:

  • 20 世纪 90 年代之前 ,上网的主要是科研人员、学者、大学生。
    • 他们主要做的事:
      • 远程登录主机
      • 传文件
      • 看新闻、收发邮件
        这些应用很有用,但圈子比较小,普通人基本接触不到"互联网"这个概念。
  • 90 年代初 ,一个新的应用"万维网(World Wide Web,简称 Web)"出现了,
    它一下子把因特网从"科研圈玩具"变成了"全民应用"。

书里说:

Web 把因特网从只是很多数据库之一的地位提升为"仅有的一个数据库网"。

意思是:

以前上网像是"到很多分散的资料室查资料";

Web 出现之后,大家越来越把"上网 = 上 Web",

感觉好像"所有信息都在 Web 上"。

对普通用户来说,Web 最吸引人的几点:

  1. "按需点播"而不是"被动收听"
    • 传统广播、电视:台里播什么你就看什么;
    • Web:你有需求时,自己打开浏览器,想看什么就搜什么、点什么。
  2. 超链接 + 搜索引擎,让你"乱逛"信息海洋很方便
    • 网页里的蓝色下划线就是超链接:
      • 一点就跳到另一个页面。
    • 搜索引擎帮你在全网里找东西,比如百度、Google。
  3. 任何人都可以很低成本当"出版人"
    • 以前要出版书、办杂志门槛很高;
    • Web 上你弄个网站、写点博客、发视频,就能"对全世界讲话"。
  4. 网页里可以嵌各种东西
    • 文本、图片、视频、音乐;
    • 后来又有 JavaScript、Java 小程序等,可以在网页里做交互;
    • 再后来 Web+协议,发展出 YouTube、基于 Web 的邮箱(Gmail)、地图(Google 地图)等等。

一句话:Web 把"信息展示、交互、应用"全揉到浏览器里了

所以成为因特网上最醒目、最典型的应用。

HTTP 概况:Web 的"说话规则"

1. HTTP 是什么?

书里说:

Web 的应用层协议是 超文本传输协议 HTTP(HyperText Transfer Protocol)。

  • 这是一个应用层协议,定义了:
    • 浏览器(客户端程序)
    • Web 服务器(服务器程序)
      之间 怎么互相发 HTTP 报文
  • RFC 1945、RFC 2616 是早期 HTTP 规范的文档,你现在知道有这么回事就行。

核心:

HTTP 就是规定"浏览器怎样向服务器要网页、服务器怎样把网页回给浏览器"的一套规则。

Web 页面到底是什么东西?

书里先讲"Web 文档 / Web 页面(Web page)"。

直觉上你觉得一个"网页"就是你看到的一屏东西,但从协议角度看:

一个 Web 页面由 很多对象(object) 组成。

  • 对象 object
    → 在这里就是"一个文件":
    • 一个 HTML 文件
    • 一张 JPEG 图片
    • 一个 Java 小程序
    • 一段视频......
  • 每个对象都有自己的 URL 地址(下面讲 URL)。

举个书中的例子:

某页面由"1 个 HTML 文本 + 5 张 JPEG 图片"构成,

那这个页面其实就包含 6 个对象

通常我们会说:

  • 页面里有一个 "HTML 基本文件(base HTML file)"
    • 它是"骨架":里面写了文字、布局;
    • 同时"引用"其他对象(图片、视频等)。

你可以把 HTML 页面想象成:

一篇 Word 文档,正文里插入了好几张图片和视频。

浏览器要显示这个网页,就得:

  1. 先拿到 HTML 基本文件;
  2. 从里面读出所有被引用对象的 URL;
  3. 再去一个个把这些对象下载回来;
  4. 最后按照 HTML 指定的位置组合在一起显示。

URL 是什么?

URL = Uniform Resource Locator,统一资源定位符。

你可以理解为"网页上每个资源的完整地址"。

格式大概是:

复制代码
http://www.someSchool.edu/someDepartment/picture.gif

拆开来看:

  • http → 使用的协议(这里是 HTTP)
  • www.someSchool.edu → 主机名(域名),说明在哪台服务器上
  • /someDepartment/picture.gif → 在服务器上文件的路径和名字

所以:

  • 主机名 + 路径 就唯一确定服务器上的一个对象文件。

浏览器拿到这个 URL,就知道:

"我要用 HTTP 协议,去连 www.someSchool.edu 这台主机,

然后向它请求 /someDepartment/picture.gif 这个对象。"

浏览器 & Web 服务器分别干嘛?

  • Web 浏览器(Web browser)

    • 比如 Chrome、Firefox、Edge 等;
    • HTTP 的客户端程序
      • 帮你发 HTTP 请求报文;
      • 收到 HTTP 响应报文,解析里边的内容,渲染成网页。

    所以我们经常把"浏览器 = 客户端"这两个词当成同义词。

  • Web 服务器(Web server)

    • 比如 Apache、微软 IIS 等;
    • HTTP 的服务器程序
      • 在服务器主机上打开端口(通常 80)等待请求;
      • 收到请求后,根据 URL 找到相应的文件或生成内容;
      • 把对象打包成 HTTP 响应报文发回去。

HTTP 定义的,就是"浏览器怎么请求,Web 服务器怎么响应"的报文格式与顺序。

HTTP 跑在 TCP 上:请求--响应过程(图 2-6)

书接着说了一件很重要的事:

HTTP 使用 TCP 作为它的支撑运输协议(不是 UDP)。

也就是说:

  1. 浏览器要获取某个网页时,先用 TCP 建立连接
  2. 建立好 TCP 连接后,才在这条连接上用 HTTP 语言来对话。

1. 建立 TCP 连接

  • 客户端(浏览器进程)先向服务器发起 TCP 连接请求;
  • 服务器同意后,一条 TCP 连接就建立起来了;
  • 这个连接是"两个套接字之间的虚拟管道":
    • 一端是浏览器的套接字;
    • 一端是 Web 服务器的套接字。

连接建立完毕之后:

客户端就可以通过这个套接字,把 HTTP 请求报文写进去;

TCP 帮它可靠地发到服务器那一端的套接字;

服务器再从套接字读出来处理。

2. 请求--响应的具体流程(配合图 2-6)

图 2-6 的意思是:

  1. 用户在浏览器里输入网址、或点击一个超链接;

  2. 浏览器如果还没和这个服务器建立 TCP 连接,就先建立一个;

  3. 浏览器通过 TCP 连接,发送一个 HTTP 请求报文

    • 请求中包含"我要哪个对象"的信息(URL 路径等)。
  4. 服务器的 HTTP 进程收到请求后:

    "HTTP 进程",指的是 "实现了 HTTP 协议的那个应用进程"

    在客户端:浏览器进程

    • 它里面有代码实现了"构造 HTTP 请求、解析 HTTP 响应"的功能;

    在服务器:Web 服务器进程(如 Apache、Nginx、IIS 等)

    • 它里面的代码实现了"接收 HTTP 请求、根据 URL 找文件、构造 HTTP 响应"的功能。

    ✅ "实现 HTTP 的程序 以进程的形式在主机上跑,我们口头上说'HTTP 客户端进程 / HTTP 服务器进程'。"

    • 根据报文里的路径找到相应文件;
    • 把文件内容打包成 HTTP 响应报文;
    • 通过 TCP 连接发回客户端。
  5. 浏览器收到响应报文:

    • 把里边的 HTML、图片、脚本等提取出来;
    • 渲染显示给用户。

对于页面里的多个对象(图片等),浏览器会发多个 HTTP 请求(是不是每个都用新的连接,后面会区分"非持久连接 / 持久连接")。

在这一节,书先告诉你大致流程,细节后面再展开。

HTTP 是"无状态协议(stateless protocol)"是什么意思?

最后这一段非常关键:"HTTP 服务器不记得你是谁。"

书里说:

服务器向客户端发送对象之后,不保存关于这个客户端的任何状态信息

你可以这么理解:

  • 你访问一个普通网站:
    • 第一次请求一个页面;
    • 第二次请求另一张图片;
    • 对服务器来说,这两次请求彼此没有记忆
      • 它不会"自然地知道"这两个请求来自同一个用户。
  • 如果你隔几分钟又发请求,服务器也不会"想起你":
    "哦,这个就是刚才那位小张" ------ 默认不会,除非通过 Cookie 等机制(后面会讲)。

为什么要设计成无状态?

  • 简单、可扩展:

    • 服务器不用给每个用户维护一堆"会话记录";
    • 当有成千上万、上百万个浏览器在访问时,服务器压力不会爆炸。
  • 它只需做一件事:

    "收到一个请求 → 返回对应对象 → 结束"。

当然,现实网站为了"记住你登录了谁、购物车里有什么",

会在 HTTP 之上用 Cookie、session 等机制 人为加"状态",

那是后一小节的内容。

在这一页里你先记住概念:

从 HTTP 协议本身的角度看,它就是一个无状态协议:
每个请求都是独立的,服务器默认不记得之前发生了什么。

因此,从你点开一个网页,到服务器响应,大致是这样:

  1. 你在浏览器地址栏敲:
    http://www.example.com/index.html
  2. 浏览器进程(HTTP 客户端)做的事:
    • 解析 URL(协议 http、主机名、路径)
    • 通过 DNS 查到服务器的 IP
    • 用 TCP 建立连接(这是传输层干的事)
    • 按 HTTP 规则 拼出一个请求报文(里面就包含路径、Host 等 → 对应 URL)
    • 把这段报文写进 TCP 套接字
  3. 操作系统里的 TCP 协议栈:
    • 把这段数据可靠地送到服务器那边的 TCP 套接字
  4. 服务器上的 Web 服务器进程(HTTP 服务器进程):
    • 从套接字里读出这段数据(一个 HTTP 请求报文)
    • 按 HTTP 规则解析出请求方法、路径等
    • 找到 /index.html 这个资源
    • 再按 HTTP 规则构造一个响应报文
    • 把响应报文写回 TCP 套接字
  5. 浏览器进程收到响应:
    • 从 TCP 套接字读出 HTTP 响应报文
    • 解析其中的 HTML、图片等
    • 绘制成你看到的网页

在这整个过程中:

  • 协议: HTTP(规则)
  • 报文: HTTP 请求报文 / 响应报文(具体数据)
  • 进程:
    • 浏览器进程:HTTP 客户端
    • Web 服务器进程:HTTP 服务器

2.2.2 非持续连接和持续连接

1. 典型场景

一个 Web 页面通常是这样:

  • 1 个 HTML 文件(骨架)
  • 10 张图片(JPEG)
  • 可能还有 JS、CSS、视频等等

浏览器想显示这个页面,就得拿到所有这些对象

问题来了:

这些对象是从同一台服务器来的,那 TCP 连接要怎么用?

有两种设计方式:

2. 非持续连接(non-persistent connection)

规则:每条 TCP 连接只服务"一对请求/响应",用完就关。

打电话类比:

  • 你问一句"在吗?" → 对方回"在" → 挂电话;
  • 再问"明天一起吃饭吗?" → 重新拨电话 → 对方回 → 再挂......

也就是:

每问一个问题,都要新打一个电话。

在 HTTP 里就是:

每拿一个对象(HTML 或图片等),都要新建一个 TCP 连接。

3. 持续连接(persistent connection)

规则:一条 TCP 连接可以在一段时间内被多次使用,连续发送多个请求、得到多个响应后再关。

打电话类比:

  • 一次电话里:
    • 问"在吗?"
    • 问"吃饭吗?"
    • 问"地点定哪?"
  • 中间不挂电话,聊完一起挂。

HTTP 里就是:

建立一个 TCP 连接后,可以在这条连接上依次请求:

HTML、图片1、图片2......直到用完再关闭。

2.2.2.1 非持续链接

采用"非持续连接"的 HTTP:一步步走一遍

书这一节先分析的是 "非持续连接的 HTTP"(HTTP/1.0 默认就是这样)。

先设定一个场景(跟书一样):

  • 某个页面有:
    • 1 个 HTML 基本文件home.index
    • 10 张 JPEG 图片
  • 这 11 个对象全在同一台服务器 www.someSchool.edu 上。

步骤 1:浏览器建立第一个 TCP 连接(为了 HTML)

  1. 客户端 HTTP 进程 (浏览器)
    发起一个到 www.someSchool.edu 的 TCP 连接,请求端口号 80(HTTP 默认端口)。
    • 这一步是 TCP 做的"三次握手",稍后讲 RTT 时会提到。
  2. 连接建立后,客户端通过这个连接的套接字 发送一个 HTTP 请求报文 ,里面写着:
    • "我要 /someDepartment/home.index 这个 HTML 文件"。
  3. 服务器 HTTP 进程 从自己的套接字里读到这个请求报文,
    在它的存储(内存或磁盘)中找到对应的文件 home.index
    把文件内容封装成 HTTP 响应报文,通过同一个 TCP 连接发回客户端。
  4. 服务器接着就通知 TCP:可以断开这条连接了
    注意:得等确认客户端已经完整收到了响应报文,TCP 才真正关闭。
  5. 客户端浏览器收到响应、把 HTML 取出来 → 然后也关闭自己这端的 TCP 连接

到这里为止:

第一条 TCP 连接只干了一件事:
传输 1 个 HTML 文件(1 个请求 + 1 个响应)。

步骤 2:浏览器解析 HTML,发现还有 10 张图片要拿

  1. 浏览器开始解析刚拿到的 HTML 文件,
    在里面看到了 10 个 <img src="..."> 这样的引用:
    也就是 10 个 JPEG 图片的 URL。

接下来的动作就是:

对这 10 张 JPEG,每一个都重复上面的 1~5 步骤。

  • 对每张图片:
    1. 新建一个 TCP 连接;
    2. 发 HTTP 请求"我要图片 X";
    3. 服务器回图片 X 的响应;
    4. 服务器关闭连接;
    5. 浏览器收完也关闭连接。

如果完全串行(一个一个来),那就是:

一共 11 次 TCP 连接(1 个 HTML + 10 个 JPEG),

每次连接只传 1 个对象。

书里后面说:

  • "每个 TCP 连接只传输一个请求报文和一个响应报文
    这就是非持久连接的典型用法。"

浏览器也可以"开多条并行的非持久连接"

书中后面提到:

  • 实际浏览器一般会开多个并行 TCP 连接 来加速:
    • 比如最多同时开 5~10 条;
    • 每条连接仍然"一个对象用完就关"。

这就好比:

你要问对方 10 个问题,不是 1 个 1 个打电话,

而是派 5 个人同时分别打 2 通电话,各问其中一些问题。

理解"非持续连接"的缺点 & RTT

书后面对非持续连接的缺点总结了两点:

  1. 连接数量太多,服务器和客户端压力大

    • 每次请求对象都要:
      • TCP 三次握手建连接;
      • 传输完再四次挥手关连接;
      • 为这个连接分配缓冲区、状态、变量......
    • 对一台热门 Web 服务器来说,成千上万用户,每个页面几十个对象 → 连接数量爆炸。
  2. 每个对象都要付出多次 RTT 的时延,页面显示慢

    这就要说明什么是 RTT(往返时间 Round-Trip Time)

    • RTT = 一个小分组从客户端到服务器再回来的时间:
      • 包括线路传播时间
      • 路由器排队、处理时间
    • 比如国内某网站 RTT 可能几十毫秒,国外几百毫秒。

对一个 HTML 文件,在非持续连接下,总耗时大约是多少?

书里简单估算(图 2-7):

  1. 先建立 TCP 连接
    • 需要一次 RTT(因为三次握手,前两步就占了一个来回时间)。
  2. 在已经建立的连接上,发送 HTTP 请求 + 收到响应的第一个字节
    • 又是一个 RTT。
  3. 再把整个 HTML 文件传完
    • 这段时间和文件大小 / 链路带宽有关。

所以粗略估算:

获取这个 HTML 文件的时间 ≈ 2×RTT + 文件真正传输时间

对于那 10 张图片,每一个都要再付一次"2×RTT + 传输时间"(除非做并行)。

所以非持久连接在时延上就比较吃亏。

2.2.2.2 持续连接

采用"持续连接"的 HTTP:怎么改进?

看到非持续连接的问题后,自然就想到:

"既然一台服务器要给同一个浏览器发一堆对象,

干嘛不只建一次 TCP 连接、然后在里面多次对话呢?"

这就是 持续连接(persistent connection)

HTTP/1.1 的默认模式就是持续连接

持续连接的基本思路

在 HTTP/1.1 的持续连接下:

  • 服务器在发送完响应后,不会立刻关闭 TCP 连接
  • 同一个客户端后续的 HTTP 请求、响应会复用这条连接。

对应到刚才的例子:

  • 客户端建立 1 条 TCP 连接;
  • 先在这条连接上请求 HTML,拿到 HTML;
  • 解析 HTML 找到 10 张图片;
  • 再在同一条连接上 依次发送 10 个"我要图片 X"的 HTTP 请求,
    服务器依次返回 10 个响应;
  • 所有对象传完后,双方再关闭连接。

整个页面(1 个 HTML + 10 张 JPEG)
只需要 1 条 TCP 连接 就搞定。

好处:

  1. 连接建立 / 关闭的开销大大减少
    • 客户端和服务器都不用为每个对象维护新的 TCP 状态;
    • 服务器不会被大量短命连接压垮。
  2. 时延减少
    • 只需为整个会话付出一次"建连接的 RTT",
    • 后面多个请求可以一个接一个发。

书里还提到一个更进一步的优化:管道化(pipelining)(在 HTTP/1.1 / HTTP/2 里):

  • 客户端可以在还没收到前一个响应时,就把后面的请求也发出去;
  • 服务器按顺序把多个响应排队发回去;
  • 这样就不用每个请求都再额外等待一个 RTT。

对服务器来说的优势

  • 对同一个客户端,一个持久 TCP 连接可以服务这个客户端的多个页面请求;
  • 多个网页的对象可以排着队在这条连接上来回;
  • 比非持久连接那种"每个对象都单独建连接"要节省很多资源。
特性 非持续连接 HTTP(HTTP/1.0 默认) 持续连接 HTTP(HTTP/1.1 默认)
TCP 连接数量 每个对象一个连接 → 1 页面 = N 个连接 多个对象共享连接 → 1 页面可只用 1 个连接
建连 / 关连开销 大:每个对象都要三次握手 + 四次挥手 小:只在整个会话开始 / 结束时做一次
时延 每个对象至少花 2 RTT + 传输时间 整个页面共用建立连接的那 1 个 RTT,后续更快
服务器压力 连接数量多,状态多,内存占用大 连接数量少,状态少,开销小
实际应用 早期 HTTP/1.0 广泛使用 现在浏览器基本默认用持久连接(HTTP/1.1、HTTP/2)

一句话记住:

非持久连接:一问一挂电话;
持久连接:一次电话聊完很多事。

2.2.3 HTTP报文格式

HTTP 报文 = 一封"纯文本格式的信"

书一上来先说两点:

  1. HTTP 报文其实就是 普通的 ASCII 文本
    → 也就是"纯文本",你用记事本看得懂的那种。
  2. 报文是由 一行一行 组成的
    • 每行的结尾有一个"回车 + 换行"(书里写 cr lf),你就把它理解成"换行"就行。
    • 最后有一行空行,专门作为"分界线"。

所以你可以把 HTTP 报文想象成:

一封格式非常严格的纯文本信,

上面有"第一行概况(抬头)+ 很多头部字段 + 空行 + 正文"。

HTTP 请求报文:浏览器发给服务器的"信"

书上给的例子(典型请求报文):

复制代码
GET /somedir/page.html HTTP/1.1
Host: www.someschool.edu
Connection: close
User-agent: Mozilla/5.0
Accept-language: fr

整体分成四部分

配合图 2-8 来看,一个 HTTP 请求报文一般是:

  1. 请求行(request line)
  2. 零行或多行首部行(header lines)
  3. 一个空行 (只有 cr lf,什么字也没有)
  4. (可选)实体体(entity body):真正的"数据内容"

在这个例子中:

  • 只有请求行 + 4 行首部行,没有实体体(因为是 GET 请求)。
2.2.3.1 请求行

请求行:最重要的一行

例子中第一行:

复制代码
GET /somedir/page.html HTTP/1.1

请求行里有 3 个"单词"(中间用空格 sp 分隔):

  1. 方法(method)GET
  2. URL 字段(准确说是路径那部分)/somedir/page.html
  3. HTTP 版本号HTTP/1.1

常见方法有哪些?

  • GET最常见,意思是"我要取资源"
  • POST:提交数据用(尤其是表单)
  • HEAD:只要头部,不要正文
  • PUT:上传 / 覆盖指定路径上的资源(一般给工具或 API 用)
  • DELETE:让服务器删除某个资源

现在你先记:浏览网页 90% 以上用的是 GET 和 POST,剩下那几个更多用在调试、接口、工具里。

2.2.3.2 首部行

首部行(Header lines):一堆"额外信息"

例子里的 4 行首部:

复制代码
Host: www.someschool.edu
Connection: close
User-agent: Mozilla/5.0
Accept-language: fr

注意格式都是:

复制代码
首部字段名: 空格 值

一个一个看:

(1) Host: www.someschool.edu

  • 表示:这次请求要访问的主机名
  • 为什么需要?
    • 因为很多服务器是一台机器上挂好多站点(虚拟主机),
      同一个 IP 可能对应 a.comb.com 等;
    • 有了 Host,服务器才能判断你要访问哪一个站点。

(2) Connection: close

  • 告诉服务器:"这次传完就关 TCP 连接,不要保持持续连接。"
  • 如果用的是持续连接(HTTP/1.1 默认),就可以不写 close,或写 keep-alive 等。

(3) User-agent: Mozilla/5.0

  • 表示客户端软件的类型 → 浏览器标识
    • 比如 Firefox、Chrome、Edge、手机浏览器等等;
  • 服务器可以根据这个决定返回不同版本页面(比如给手机的简化版)。

(4) Accept-language: fr

  • 表示:希望服务器给我返回"法语(French)版本"的资源"
  • 如果服务器有多语言版本,就可以按这里的偏好来选;
  • 如果没有,就按默认语言返回。

你现在可以把"首部行"理解成:

一封信里面贴的小标签:谁寄的、希望用什么语言、用什么方式传......
不是必须都有,但很多都很常见、很有用。

2.2.3.3 实体体

实体体(entity body):真正要"装东西"的地方

在图 2-8 中,你能看到在空行之后还有一个"实体体"。

对于请求报文:

  • GET 请求:一般没有实体体;
  • POST 请求 :常常有实体体,比如:
    • 你在网页上提交表单(用户名、搜索关键字等),
      这些表单数据就被放在请求报文的实体体里发给服务器。

举个小例子(简化版 POST):

复制代码
POST /submit HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

username=tom&pwd=123456

空行下面这句 username=tom&pwd=123456 就是实体体。

书里还提到:

  • HEAD:像 GET,但服务器只返回"响应头部",不带真正内容
    → 常用于测试链接是否有效、获取对象的属性而不下载全文。
  • PUT:客户端给服务器上传一个对象,放到某个指定路径
    → 适合"发布工具"或 API 使用,一般用户浏览网页不会直接用到。
  • DELETE:让服务器删除某个路径上的对象
    → 也多用于 API,而不是普通网页浏览。

你当前阶段先牢牢记住:GET / POST / HEAD,以后再细看 PUT/DELETE 就够用。

HTTP 响应报文:服务器回给浏览器的"信"

书里的响应报文例子:

复制代码
HTTP/1.1 200 OK
Connection: close
Date: Tue, 18 Aug 2015 15:44:04 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Tue, 18 Aug 2015 15:11:03 GMT
Content-Length: 6821
Content-Type: text/html

(data data data data data ...)

整体结构(配合图 2-9)

跟请求报文很像,也分 3 大块:

  1. 状态行(status line):第一行
  2. 若干个首部行(header lines)
  3. 一个空行 + 实体体(entity body):对象本体数据
2.2.3.4 状态行

状态行:告诉你结果咋样了

复制代码
HTTP/1.1 200 OK

三个部分:

  1. 版本号HTTP/1.1
  2. 状态码200
  3. 状态短语OK(对人看的简单说明)

状态码 + 短语 = 告诉浏览器:"这次请求的结果如何"

书里列了几个常见的,你平时在浏览器里也经常见到:

  • 200 OK
    → 请求成功,一切正常,数据在响应体里。
  • 301 Moved Permanently
    → 请求的对象已经永久搬家了;
    服务器会在 Location: 首部里告诉你新的 URL;
    浏览器就会自动重定向到新地址。
  • 400 Bad Request
    → "请求有问题",比如报文格式乱七八糟,服务器看不懂。
  • 404 Not Found
    最有名 :不存在的网页;
    URL 指向的对象在服务器上没找到。
  • 505 HTTP Version Not Supported
    → 客户端用的 HTTP 版本,服务器不支持。

以后你在浏览器 F12 开发者工具里,也能看到这些状态码。

2.2.3.5 响应首部行

响应首部行:各种说明信息

复制代码
Connection: close
Date: Tue, 18 Aug 2015 15:44:04 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Tue, 18 Aug 2015 15:11:03 GMT
Content-Length: 6821
Content-Type: text/html

我们挨个解释:

(1) Connection: close

  • 告诉客户端:"这次发完我就要关掉 TCP 连接了"
  • 配合前面的请求说的 Connection: close 一起使用。

(2) Date: ...

  • 表示 服务器"产生这条响应"的时间(不是文件的创建时间);
  • 方便浏览器和缓存系统判断新旧。

(3) Server: Apache/2.2.3 (CentOS)

  • 表示运行在服务器上的 Web 服务器软件:
    • 比如 Apache、Nginx、IIS 等;
  • 有时也会带版本和操作系统。

(4) Last-Modified: ...

  • 表示这个对象在服务器上"最后被修改"的时间;
  • Web 缓存(如浏览器缓存、代理缓存)会用这个时间判断:
    • 本地缓存是否落后于服务器版本;
    • 如果没变,就可以不重新下载,节省流量。

(5) Content-Length: 6821

  • 表示接下来的实体体有多少 字节
  • 浏览器根据这个值知道什么时候"读完了"。

(6) Content-Type: text/html

  • 告诉浏览器:实体体里的数据是什么类型
    • text/html → HTML 文本
    • image/jpeg → JPEG 图片
    • video/mp4 → MP4 视频
    • application/json → JSON 数据
      (后面你都会见到)

浏览器就靠 Content-Type 决定:

  • 这是网页 → 就按 HTML 渲染
  • 这是图片 → 就以图片方式显示
  • 这是视频 → 播放器处理
2.2.3.6 实体体

例子最后:

复制代码
(data data data data data ...)

这里就是真正的 HTML 文件内容(或者图片、视频等)。

  • 请求报文:实体体一般只有在 POST 等方法时出现;
  • 响应报文 :实体体是最常见的,几乎所有"有内容的响应"都有。

Telnet 小例子:说明 HTTP 就是"纯文本对话"

书最后给了一个小技巧:

你可以不用浏览器,而是用 telnet 工具 手打 HTTP 请求报文 来看响应。

书上的例子(简化):

复制代码
telnet gaia.cs.umass.edu 80

GET /kurose_ross/interactive/index.php HTTP/1.1
Host: gaia.cs.umass.edu
  • telnet gaia.cs.umass.edu 80
    → 建立到服务器 80 端口(HTTP 端口)的 TCP 连接;
  • 然后你自己敲两行请求(按两次回车结束):
    • 第一行:请求行
    • 第二行:Host 头
    • 最后一行空行(就是只按回车)

服务端就会按照 HTTP 协议给你回一个完整的响应报文,你能直接在终端里看到。

这个例子想说明:
HTTP 报文本质上就是"纯文本 + 换行组成"的协议

只要你照着格式写,人肉也能跟服务器"聊 HTTP"。
www.example.com

我们把它"剁开":

  • www
  • example
  • com

这三个加在一起,叫做一个 完整的域名 / 主机名(专业叫 FQDN)。

域名(domain name) 是一个"名字空间",用来划分网络中的区域,就像:

  • com:顶级域(像"某个大国")
  • example.com:在 com 下面划出来的一个"地盘",可以理解成"一个公司/组织的地盘"

通常我们说"我注册了一个域名 example.com",

指的是 这个整体 example.com,你可以在它下面再分很多名字:

  • www.example.com
  • mail.example.com
  • api.example.com
  • blog.example.com
  • ...

这些统统都属于 example.com 这个域下面。

简单比喻:

  • com 像"国家";
  • example.com 像"这个国家里的一个城市";
  • www.example.com 像"这个城市里的某栋楼"。

什么是「主机名(hostname)」?

主机名 可以理解为:

"域名里面某一台具体主机(服务器)的名字"。

www.example.com 里:

  • 整串 www.example.com 常被叫作"主机名 / 主机的域名";
  • 有时也把最左边这段 www 看作"主机名这部分",
    右边的 example.com 叫"所属的域"。

所以很多教材里会这么说(大概的意思):

  • example.com
  • 主机名www
  • 完整主机名www.example.com

不过在实际 Web 开发里,为了省事,大家经常把"域名"和"主机名"混着叫

严格一点说:

  • 域名 更偏向 "名字空间(example.com)";
  • 主机名 更偏向 "具体机器的名字(www.example.com 这一整串)"。

但你可以这样记:

对于 HTTP 头里的 Host:
你就把它理解成:把"浏览器地址栏里那段域名字符串照抄进去"

比如 Host: www.example.com

总结:

主机名和域名不完全一样,但关系很近:

  • 域名:example.com(以及它下面的整体名字空间);
  • 主机名:域名里的某台主机,例如 www.example.com

在很多 Web 场景中 ,大家说"域名""主机名"经常指同一串东西(比如 www.xxx.com),

所以你看到书上、代码里混着用,不用太纠结------知道它们都指"那个用来访问网站的名字"就可以。

真正对网络设备来说,最关键的是:

  • DNS 把域名 / 主机名 → 解析成 IP;
  • 浏览器在 HTTP 请求里把这个名字写到 Host: 头里,
    服务器凭这个判断"你要的是哪个站点"。

2.2.4 用户与服务器的交互:cookie

为什么要有 cookie?------因为 HTTP"失忆"

  • HTTP 协议有个特点:无状态(stateless)

    意思是:

    服务器只把每一个 HTTP 请求当成"全新的陌生人",处理完就忘了,不记得你之前来过。

  • 现实网站需要什么?

    • 记住你加过哪些商品到购物车
    • 记住你已经登录了,不用每个页面都重新输账号密码
    • 给你"猜你喜欢""历史浏览记录"
      ......
      这些都需要记住"你就是上一次那个用户"
  • 矛盾点:

    • HTTP:我只会收发请求和响应,我不记人
    • 网站:我必须记住用户的状态
      → 于是就想了个"外挂记忆方法"------cookie

你可以把 cookie 理解成:

"网站发给你的一张身份小纸条(编号),浏览器帮你保管,下次再来时,你把这张纸条带给网站,让它知道:'还是我'。"

cookie 的四个组成部分(书里那句"4 个组件")

书里说 cookie 技术有 4 个组件,对照着说人话:

  1. 在 HTTP 响应报文中的一个 cookie 首部行

    • 服务器第一次见到你,会在回给你的 HTTP 响应里加一行类似:

      复制代码
      Set-Cookie: 1678
    • 这个叫 Set-Cookie 首部,意思是:

      "喂,浏览器,请帮我把这个编号 1678 存起来,之后你再来找我,就带上它。"

  2. 在 HTTP 请求报文中的一个 cookie 首部行

    • 以后你再访问这个网站,浏览器会在发出去的 HTTP 请求里自动加一行:

      复制代码
      Cookie: 1678
    • 这叫 Cookie 首部,就相当于你进门先递上"会员卡"。

  3. 在用户端系统中保留的一个 cookie 文件,由浏览器进行管理

    • 浏览器把服务器给的 cookie(比如 1678)存到你电脑本地的一个文件里
    • 文件里会记:哪个网站、给了什么 cookie、什么时候过期等等。
    • 所以后面你关机、重启浏览器,只要没过期,浏览器还是能记得。
  4. 位于 Web 站点后端数据库中的一个表项

    • 服务器那边也有一个 数据库表 ,里边有很多行:
      • 用户 ID(比如 1678)
      • 这个 ID 对应用户看过的页面
      • 购物车里有哪些商品
      • (如果你注册了)姓名、地址、邮箱、信用卡号......
    • 每次服务器收到 Cookie: 1678,就去这个表里查"ID=1678"这行,知道你是谁、之前干过什么。

这四个一起配合,就实现了:

"浏览器和服务器通过一个小小的编号,在无状态的 HTTP 上,自己搞出了'有记忆的会话'。"

书上图 2-10 用的是 Susan 访问 eBayAmazon,我们按时间线说:

1)第一次上网:访问 eBay(和 cookie 没太大关系)

  • Susan 在家用 PC(浏览器是 IE)上网;
  • 先访问 eBay:浏览器发出普通的 HTTP 请求报文;
  • eBay 返回一个普通的 HTTP 响应报文(书里没细讲 cookie,主要是铺垫)。

2)第一次访问 Amazon:服务器创建 cookie

现在 Susan 访问 Amazon:

  1. Susan 的浏览器发出 HTTP 请求 → Amazon 服务器

    • 这是她第一次访问 Amazon,所以请求里还没有 cookie
  2. Amazon 服务器给她分配一个唯一编号

    • 比如:1678(书里的例子)
    • 同时在自己后台数据库里新建一行:
      • 用户 ID = 1678
      • 其他信息先空着(以后慢慢填:她看过什么、加过什么商品等)
  3. Amazon 在响应报文里加上 Set-Cookie

    • 返回的 HTTP 响应多了一行首部:

      复制代码
      Set-Cookie: 1678
  4. 浏览器收到响应 → 把 cookie 写进本地文件

    • IE 浏览器看到 Set-Cookie: 1678 这一行,就会在它管理的 cookie 文件里加一条记录:
      • 网站:amazon
      • cookie 值:1678
    • 如果她以前访问过 eBay,那文件里已经有 eBay 的 cookie 表项了,现在只是多了一条 Amazon 的。

小结一下:

  • 服务器:"这个叫 1678 的人,以后你再来报这个号码。"
  • 浏览器:"好,我帮你把 1678 记着。"

一段时间后再访问 Amazon:cookie 发挥作用

假设一周之后,Susan 又来 Amazon:

  1. 浏览器再次向 Amazon 发 HTTP 请求

    • 在发请求前,浏览器会先查本地 cookie 文件:

      "有给 amazon.com 的 cookie 吗?"

      有 → 1678

    • 所以请求报文里自动加上这一行:

      复制代码
      Cookie: 1678
  2. Amazon 服务器收到请求,看到 Cookie: 1678

    • 服务器就知道:

      "哦,这不是新访客,这是上次的那位 1678 用户。"

    • 然后服务器去后端数据库ID=1678 这一行,知道:

      • 她之前看过哪些商品
      • 购物车里有没有东西
      • 她的偏好是什么
  3. "cookie 特定动作"是啥?

    • 书上图里右边写的是"cookie 特定动作",意思是:
      • 根据这个用户 ID 做一些个性化操作,比如:
        • 展示"你可能还喜欢..."
        • 把上次放在购物车里的商品继续显示出来
        • 记录她这次又访问了哪些页面
  4. 如果她在 Amazon 注册了账号

    • 她填:姓名、电子邮箱、邮寄地址、信用卡号等等;

    • 服务器会把这些信息写到数据库里 ID=1678 的那一行;

    • 以后只要看到 Cookie: 1678

      • 就知道这是 "Susan",地址是 XXX,信用卡是 XXX;

      • 于是出现书里说的一键购物(one-click shopping):

        再买东西时不用重新输姓名、卡号、地址。

所以:

虽然 Amazon 不一定知道她真名叫 Susan(除非她注册时填了),

只要看到 1678,就能知道"这个人过去都干了啥"。

cookie 用来"标识用户"和"建立会话"

书里最后一段讲的是 cookie 的用途,可以归纳成两大类:

1)标识用户(知道"你是谁")

  • 每个用户第一次来,服务器给一个唯一 ID(就像会员编号)。
  • 以后所有请求都带这个 ID,服务器就可以"认出"你,并根据数据库信息提供个性化服务:
    • 推荐系统
    • 历史浏览
    • 购物车
    • 语言偏好(总给你中文版界面)......

2)在无状态 HTTP 上建立"会话"(session)

  • 假设一个 Web 系统:例如 Web 邮箱(早期 Hotmail、现在的 QQ 邮箱、Gmail 等)。

  • 用户登录时:

    1. 输入账号密码;

    2. 服务器验证通过后,生成一个专属于这次登录的 cookie(比如 session id),在响应中:

      复制代码
      Set-Cookie: session_id=xyz123
    3. 浏览器保存这个 cookie。

    4. 后面的所有请求(收邮件、发邮件、打开设置页面等),浏览器都会自动加上:

      复制代码
      Cookie: session_id=xyz123
    5. 服务器看到这个 session_id,就知道:

      "你已经登录了,是某某账号",所以不再让你每一步都输入密码。

  • 这就叫:

    "在无状态的 HTTP 之上,构建一个有状态的用户会话"。

最后一点:隐私与争议

书的最后一小段在提醒:cookie 也有问题:

  • 结合 cookie 和你主动填写的账号信息(姓名、地址、电话等),
    网站可以知道你:
    • 常买什么
    • 常访问哪些页面
    • 活动时间习惯......
  • 如果这些信息被卖给第三方公司,就可能用来:
    • 精准广告投放
    • 甚至更严重的隐私泄露

所以:

  • 一方面,cookie 让上网体验更方便(无需反复登录、购物车不会丢、个性化推荐);
  • 另一方面,它又 带来隐私风险,所以才会有各种争议、法律规定、浏览器设置(比如清除 cookie、禁止第三方 cookie 等)。

2.2.5 Web缓存

Web 缓存是啥?(先有个直观概念)

书上说:Web 缓存(Web cache)也叫代理服务器(proxy server)

你可以把它想象成:

学校门口的"小卖部仓库",专门帮你存放一些常用的零食,这样你就不用每次都跑到很远的总仓库去拿。

在网络里:

  • 初始服务器:真正放网页、图片的那个网站服务器(比如 www.someschool.edu)。
  • Web 缓存 :离你比较近的一台机器,提前把热门的网页副本存到自己的硬盘上
  • 客户端:你的电脑、手机,浏览器就是客户端程序。

工作目标只有两个字:加速、省流量

通过 Web 缓存访问网页的流程(图 2-11)

书上举的例子:浏览器要访问
http://www.someschool.edu/campus.gif

这是一张图片。

如果有 Web 缓存,流程变成这样(对应书里 1~4 步):

第 1 步:浏览器先连 Web 缓存,而不是连真正的网站

  • 原来:
    你上网 → 浏览器直接连 原始服务器www.someschool.edu),发 HTTP 请求。
  • 现在:
    配置好之后,浏览器会 先把请求发给 Web 缓存

类比:你本来是自己跑去总仓库拿东西,现在改成:

先问"学校门口仓库"有没有。

第 2 步:Web 缓存先看自己这里有没有这个对象

  • Web 缓存收到请求后,会检查:
    • "我硬盘里有没有 campus.gif 这张图的副本?"
  • 如果有,说明以前别人访问过它,缓存已经存了一份。
    • 那么:缓存直接给你返回 HTTP 响应报文,把图片发给你
    • 整个过程就结束了,根本不用访问远处的原始服务器

这就叫 命中(hit)

"你要的东西我这儿正好有。"

第 3 步:缓存里没有,就再去找原始服务器

  • 如果缓存里没找到这张图,那叫 未命中(miss)
  • 这时候,缓存自己扮演"客户端"的角色:
    • 它再去和 原始服务器 www.someschool.edu 建 TCP 连接;
    • 向它发 HTTP 请求:"给我 campus.gif"。

类比:学校小仓库没有,就帮你打电话去总仓库要货。

第 4 步:拿到对象后,缓存一边存,一边转交给浏览器

  • 原始服务器把 campus.gif 发给 Web 缓存;
  • Web 缓存做两件事:
    1. 把图片存到自己的磁盘(这样以后别人再要,就可以命中);
    2. 同时把图片回给最开始的浏览器

之后别的同学再访问同一个图片时,缓存就能直接给他们,速度就非常快。

Web 缓存到底算客户端还是服务器?

书上有一句话:

Web 缓存既是服务器又是客户端。

怎么理解?

  • 浏览器 来说:
    它就是一个"服务器"------浏览器给它发 HTTP 请求,它回 HTTP 响应。
  • 原始服务器 来说:
    它又是一个"客户端"------它再去给真正的网站发 HTTP 请求。

所以它夹在中间:"对内像服务器,对外像客户端"。

为什么要在因特网上部署 Web 缓存?(两个原因)

书上说了 两个主要原因

1)减少用户等待时间(响应时间)

  • 如果客户端和 Web 缓存之间是一个高速局域网(很常见:你在学校、公司内网,缓存也在本单位机房);
  • 而客户端到原始服务器之间是比较慢的链路(穿越公网,甚至跨国);

那只要缓存里有你想要的对象,它就可以:

本地高速网上,用很短的时间把内容给你 ------ 这比跨越大半个地球去原始服务器快多了。

2)减少"跨公网"的通信量,省钱

  • 很多单位(学校、公司)接外网的链路是要花大钱租的;
  • 所有访问外部网站的流量都挤在这根"因特网接入链路"上;
  • 如果某些资源可以在 单位内部的缓存 就搞定,就不用把所有请求都发到公网去。

好处:

  • 外网链路上的流量减少了;
  • 单位就不用急着花钱升级带宽;
  • 整体费用下降。

小结:
Web 缓存 = 让常见请求"就近解决",又快又省钱。

带数值的例子(图 2-12):看清楚瓶颈在哪

书上画了一个场景:

有一个**机构网络(内网)**通过一条 15Mbps 的接入链路接到因特网,因特网上有很多初始服务器。

局域网是 100Mbps,很快;接入链路只有 15Mbps,相对慢很多,所以是瓶颈

假设条件(书里给的数字)

  • 一个对象(网页、图片)的平均大小:1Mb(注意是 Mbit,不是 MByte)。
  • 机构内所有用户访问这些初始服务器的平均速率:15 个请求/秒
  • 从"机构网络出口路由器"把一个 HTTP 请求报文发到外面,到收到 HTTP 响应报文为止(走公网那一段)平均要 2 秒
    • 书里把这叫因特网时延:大致 2s(包括跨公网的排队、传输等)。

1)先看内部局域网的压力

书用"流量强度"这个概念(可以理解成"忙碌程度",越接近 1 越拥堵)。

局域网流量强度:
流量强度 = 到达速率 × 平均业务量 链路速率 \text{流量强度} = \frac{\text{到达速率} \times \text{平均业务量}}{\text{链路速率}} 流量强度=链路速率到达速率×平均业务量

代入数字:

  • 到达速率:15 请求/秒;
  • 每个请求平均 1Mb;
  • 局域网带宽:100 Mbps;

所以:
ρ 局域网 = 15 × 1 100 = 15 × 0.01 = 0.15 \rho_{\text{局域网}} = 15 \times \frac{1}{100} = 15 \times 0.01 = 0.15 ρ局域网=15×1001=15×0.01=0.15

0.15 远小于 1,说明局域网其实比较空,排队时延很小,可以忽略。

2)看看接入链路的压力(真正的瓶颈)

接入链路带宽:15 Mbps,所以:
ρ 接入链路 = 15 × 1 15 = 15 × 0.066 6 ‾ ≈ 1 \rho_{\text{接入链路}} = 15 \times \frac{1}{15} = 15 \times 0.066\overline{6} \approx 1 ρ接入链路=15×151=15×0.0666≈1

也就是 流量强度接近 1,意味着:

  • 这条链路几乎一直满负荷地在跑;
  • 一旦稍微再多几个人访问,就会出现 排队时间疯狂变长 的情况;
  • 书里说:可能导致某些请求的总延迟达到几十秒,非常难受。

所以,现在的响应时间 ≈

  • 局域网延迟(很小,可以忽略),再加上
  • 接入链路 + 公网的延迟(平均算成约 2 秒,甚至可能更高)

方案一:升级接入链路带宽(图 2-12 分析后半)

想法:把 15Mbps 升级到 100Mbps。

新的流量强度:
ρ = 15 × 1 100 = 0.15 \rho = 15 \times \frac{1}{100} = 0.15 ρ=15×1001=0.15

不再接近 1,等待排队时间就很小了。这样:

  • 整体响应时间就主要由那 2 秒因特网时延决定;
  • 用户平均等待时间 ≈ 2 秒;

问题在于:

这意味着机构必须花很多钱,把对外的接入链路从 15Mbps 升级到 100Mbps ------ 很贵。

所以书里说:这是"代价很高的方案"。

方案二:不升级带宽,改用 Web 缓存(图 2-13)

现在考虑另一种解法:在机构网络里安装一个 Web 缓存

假设:

  • Web 缓存的 命中率 (hit rate)是 0.4。
    意思是:40% 的请求,直接在缓存里就找到了,不用出外网。
  • 剩下 60% 的请求还是要去外面的原始服务器。

1)命中时的延迟

  • 客户端和缓存之间,是 100Mbps 的局域网;
  • 书里假设:在这种条件下,从发出请求到收到响应,延迟约 0.01 秒 = 10ms
  • 这非常快,几乎秒开。

2)未命中时的延迟

  • 60% 的请求要去原始服务器走公网;
  • 此时时延 ≈
    • 局域网那一小点(0.01 秒),加上
    • 因特网上 2 秒左右的延迟;
  • 所以约为:2.01 秒

3)平均响应时间怎么算?

有两类请求:

  1. 40% 命中:每次 0.01 秒;
  2. 60% 未命中:每次 2.01 秒。

平均时间:
T avg = 0.4 × 0.01 + 0.6 × 2.01 T_{\text{avg}} = 0.4 \times 0.01 + 0.6 \times 2.01 Tavg=0.4×0.01+0.6×2.01

我们算一下:

  • 0.4 × 0.01:
    • 4 × 1 = 4;
    • 小数点后 3 位 → 0.004 秒。
  • 0.6 × 2.01:
    • 2.01 × 6 = 12.06;
    • 再除以 10 → 1.206 秒。

加在一起:
T avg = 0.004 + 1.206 = 1.210 秒 T_{\text{avg}} = 0.004 + 1.206 = 1.210 \text{ 秒} Tavg=0.004+1.206=1.210 秒

差不多是 1.2 秒多一点

书里说:"略大于 1.2 秒"。

4)对比两个方案

  • 方案一:花大钱把接入链路从 15Mbps 升到 100Mbps
    → 响应时间 ≈ 2 秒。
  • 方案二:不加带宽,只在机构内部加一个 Web 缓存
    → 响应时间 ≈ 1.2 秒,甚至比方案一还快 ,而且:
    • 对外链路的流量也减小了(只有 60% 请求会出外网);
    • 单位不必升级昂贵的接入链路。

所以书里说:第二种方案在时延和成本上都更优

再往前走一步:从单个缓存到 CDN

最后一段提到:内容分发网络(CDN, Content Distribution Network)

意思是:

不只是某个单位自己弄一台缓存,而是专门的 CDN 公司在全球各地布满很多缓存服务器,把热门内容(视频、图片、网页)提前分发到各地。

这样用户访问时:

  • 实际上是从最近的 CDN 节点拿数据;
  • 不是每次都跑到"源站"(原始服务器)去;
  • 典型例子:听歌的、看视频的(书里举了像 Akamai、Netflix 的专用 CDN 等)。

CDN 就是把 "Web 缓存" 这件事做成了一个大规模的商业服务。

1. "公网"是啥?

而客户端到原始服务器之间是比较慢的链路(穿越公网,甚至跨国)

这里的 公网 就是指 整个 Internet(因特网)

  • 所有人都能连的那张全球大网;
  • 网站服务器、别人的电脑、国外的机房都在这张网里;
  • 路由器一跳一跳地把你的数据包送过去。

你在家打开淘宝、B 站,其实就是通过运营商,把数据发到 公网 里,再转到网站服务器那边。


2. "外网"是什么意思?

很多单位(学校、公司)接外网的链路是要花大钱租的

这里的 外网 = "外面的网络" = Internet 公网,只是站在单位的角度说的:

  • 学校/公司 内部 的网络叫"内网"(下面讲);
  • 内网要想访问全世界的网站,就得通过一条"接到外面 Internet 的线";
  • 这条线(接入链路)通常是向电信/移动/联通等运营商 花钱租来的专线带宽
  • 带宽越大越贵,所以书里说"要花大钱租"。

简单记:

站在单位角度:

  • 内网 = 我们自己单位里面的那张局域网
  • 外网 = 单位外面的 Internet 公网

3. "内网"是什么?

有一个机构网络(内网)通过一条 15Mbps 的接入链路接到因特网

内网(机构网络)就是:

  • 某个单位内部自己搭的网络:学校、公司、医院......
  • 通常是一个 局域网(LAN):交换机、路由器把办公室、机房、教室里的电脑连在一起;
  • 内网里机器之间走的是 内部链路(比如 100Mbps 的局域网),很快;
  • 但从内网出去到 Internet,要经过那条 接入链路(这里是 15Mbps),这条线就是瓶颈。

你可以这样想:

  • 内网:学校里的教学楼 + 实验室之间的网线 / Wi-Fi;
  • 接入链路:学校出口连运营商机房的那根粗网线;
  • 外网/公网:全世界的网站所在的大网。

"局域网流量强度"里的"流量"是啥?

先把几个词分开:

4.1 流量 = 每秒有多少数据在这条线里走

  • 比如 1 秒钟里,传了 10 Mb 的数据,就可以说这 1 秒的"流量"是 10 Mb;
  • 往往用 Mb/s、Gb/s 这类单位。

在书里,它是这么算的(大概意思就行):

"每秒来了多少个请求" × "每个请求平均多大" = 每秒总数据量

再除以"链路带宽" → 看这条线被占用了多少比例

4.2 流量强度(负载程度)

书里用的公式(你不用死记,理解就好):
流量强度 = 到达速率 × 平均业务量 链路速率 \text{流量强度} = \frac{\text{到达速率} \times \text{平均业务量}}{\text{链路速率}} 流量强度=链路速率到达速率×平均业务量

  • 分子 = 每秒要"处理的工作量";
  • 分母 = 这条线每秒最多能处理多少;
  • 结果是 0~1 之间的数,越接近 1,说明越"挤",排队越厉害,延迟越大。

在那段例子里:

  • 内部局域网:算出来是 0.15 → 很空,延迟可以忽略;
  • 接入链路:算出来很接近 1 → 很挤,是瓶颈。

你可以粗暴理解:

流量强度 = "这条线有多忙"

  • 0.1:很闲
  • 0.5:一般
  • 0.9:爆满,随时堵车

2.2.6 条件GET方法

咱们接着 Web 缓存往下看"条件 GET 方法"。其实就是解决一个很现实的小问题:

缓存里的东西会不会变旧?如果旧了怎么办?

我分成 4 块讲:

  1. 为什么需要"条件 GET"
  2. 什么叫"条件 GET"(If-Modified-Since)
  3. 书上的完整例子一步一步走
  4. 304 状态码表示什么意思、有什么好处

为什么需要"条件 GET"?

前面说过:

  • 有了 Web 缓存,很多对象(网页、图片)可以直接在缓存里拿,速度很快。
  • 但有一个风险:缓存里的副本也许已经过时
    • 比如原始服务器上的图片被改了,缓存里还是老版本。

于是就有两个需求:

  1. 尽量用缓存(快、省流量)
  2. 又要确认缓存里的东西是不是最新的

HTTP 给的解决办法就是:条件 GET

这个机制允许缓存去问原始服务器:

"这段时间你有没有改过这个文件?

如果改过,就把最新的发给我;

如果没改过,就不用浪费带宽再传一遍。"

什么叫"条件 GET"?

书上给了一个严格的定义:

只要满足:

  1. 请求方法是 GET
  2. 首部里有 If-Modified-Since: ......

那这个请求就是一个 条件 GET 请求

理解成大白话就是:

"如果(If)在某个时间之后被修改过(Modified-Since),

你才给我真正的数据;

如果从那之后没改过,就告诉我'没变',就行。"

这里有两个关键首部要配合使用:

  1. Last-Modified:

    • 服务器在响应里告诉你:

      "这个文件最后一次修改的时间是 XXX。"

  2. If-Modified-Since:

    • 之后缓存再去问的时候,会把这个时间带回去:"如果从这个时间之后改过,就给我新版本。"

你可以把它想成:

  • 第一次见面:服务器说"我这份文件是 9月9日 09:23:24 修改的"。
  • 后面再问:缓存说"如果从 9月9日 09:23:24 之后你改过,就告诉我并给我新版;否则就说没改过。"

我们来按时间顺序走一遍书上的例子。

第一次:缓存向原始服务器要 kiwi.gif

"一个代理缓存(Web cache)代表浏览器向 Web 服务器发送请求报文"

请求:

复制代码
GET /fruit/kiwi.gif HTTP/1.1
Host: www.exotiquecuisine.com

含义:

  • 请求方法:GET
  • 要的资源:/fruit/kiwi.gif
  • 主机:www.exotiquecuisine.com

服务器回复一个普通的 200 OK 响应

复制代码
HTTP/1.1 200 OK
Date: Sat, 3 Oct 2015 15:39:29
Server: Apache/1.3.0 (Unix)
Last-Modified: Wed, 9 Sep 2015 09:23:24
Content-Type: image/gif

(data data data ...)

重点看两个时间字段:

  • Date :这次响应发出的时间
    → "我现在回复你的时间是 10 月 3 日 15:39:29"
  • Last-Modified :对象最后一次被修改的时间
    → "这张 kiwi.gif 最后改动是在 9 月 9 日 09:23:24"

接下来缓存会做两件事:

  1. 把图片数据转发给自己的浏览器客户端;
  2. 在本地存一份副本 ,同时把这张图片的 Last-Modified 时间也记下来

一周后:另一个用户通过同一个缓存再访问 kiwi.gif

这时缓存里已经有 kiwi.gif 的副本,但不确定这一个星期里原始服务器有没有改过这张图。

于是缓存不会直接把老副本给用户,而是先对原始服务器发一个 条件 GET确认是否需要更新

请求变成:

复制代码
GET /fruit/kiwi.gif HTTP/1.1
Host: www.exotiquecuisine.com
If-Modified-Since: Wed, 9 Sep 2015 09:23:24

注意这里:

  • If-Modified-Since 的值 正好等于前一次响应的 Last-Modified
    也就是:"如果你在 9 月 9 日 09:23:24 之后改过它,就发给我新的对象。"

服务器收到后会做判断:

  • 看看 kiwi.gif 是不是在这个时间之后被改过。

书里假设:从 9 月 9 日到现在都没有改过

所以服务器会返回一个特殊的响应:

复制代码
HTTP/1.1 304 Not Modified
Date: Sat, 10 Oct 2015 15:39:29
Server: Apache/1.3.0 (Unix)

(empty entity body)

这里有几个关键点:

  1. 状态码是 304 Not Modified
    • 意思是:"没改过,你本地那份还可以用。"
  2. 没有实体主体(empty entity body)
    • 也就是说,这个响应里 不再发送图片数据本身
    • 只发了响应行和一些首部,数据量非常小。

缓存一看:

  • 哦,304 → 懂了,服务器说"没改过";
  • 那我就继续使用自己缓存盘里的那份 kiwi.gif;
  • 并把这份数据转发给这次发起请求的浏览器。

整个过程结束,用户拿到图片,而且:

  • 没有重复下载完整的图片(节省带宽);
  • 延迟也更小(header 很短,传得快)。

如果服务器发现"已经改过"会怎样?

很简单:它就像第一次那样再发一次 200 OK + 新数据 + 新的 Last-Modified

缓存更新本地副本,再转发给浏览器。

1)解决的问题

  • 只用缓存:可能用到的是过期的老内容
  • 不用缓存、每次都重新下载:浪费带宽、慢

条件 GET 提供了一个折中方案:

  • 每次访问前先问一下:"最近有没有改过?"
  • 如果没改,就直接用缓存;
  • 如果改了再下载新的。

这样:

  • 保证用户看到的是最新的内容;
  • 又尽量避免重复传输大文件。

2)对网络的好处

特别是当对象"很大"的时候(例如视频文件、高清图片):

  • 如果每次都完整从服务器取一遍,浪费巨量带宽;
  • 有了条件 GET,大多数情况下只需一次轻量级的头部响应(304),就可以确认没变

所以书上说:

"包含该对象本会浪费带宽,并增加用户感受到的响应时间,特别是如果该对象很大时更是如此。"

相关推荐
西幻凌云8 小时前
了解计算机网络的“物理根基”——物理层与数据链路层
网络·网络协议·计算机网络·数据链路层·物理层
取名真是12 小时前
快速了解集线器
计算机网络
不染尘.13 小时前
计算机网络概述
网络·计算机网络
小李独爱秋13 小时前
Cisco Packet Tracer仿真实验 12:运输层端口
网络·网络协议·计算机网络·智能路由器·仿真实验
Mu.3872 天前
计算机网络模型
网络·网络协议·计算机网络·安全·http·https
0和1的舞者3 天前
《网络编程核心概念与 UDP Socket 组件深度解析》
java·开发语言·网络·计算机网络·udp·socket
没书读了3 天前
计算机网络-考前记忆清单
计算机网络
Protein_zmm3 天前
第一章 计算机网络和因特网(下)
服务器·计算机网络·php
矶鹬笛手4 天前
(2.1) 信息技术及其发展
sql·计算机网络·c#