python-进阶-网络概论(三)

什么是 HTTP

身为一个 Web 工程师,最需要熟悉的就是应用层的 HTTP 协议了,这是目前最广为流行的应用层协议。

HTTP,是指由客户端发送 HTTP Request (请求)到服务器端,然后服务器端返回 HTTP Response (回复)。HTTP 一定是先从客户端开始,服务器端不能直接发送信息给客户端。(Hypertext(超文本) 意思是拥有超链接(一点就跳去另一个网页)能力的文件)

举个具体例子:

当你在浏览器输入www.jianshu.com时,浏览器就会将以下的信息装进 TCP/IP 数据包内,然后通过因特网传到服务器:

# 这个 Request 只有 Header,没有 Payload
...
> GET / HTTP/1.1
> Host: www.jianshu.com
...

服务器在接收到以上的信息后,会回传以下的 HTTP Response 给浏览器:

# 这个 Response 的前两行是 Header,空一行之后是 Payload,也就是 HTML 内容
< HTTP/1.1 200 OK
< Content-Type: text/html
...
<html>
<h1>这里是HTML源码...</h1>
<p>...</p>
</html>
...

Tips

因特网(Internet) 和 Web(万维网) 的技术差别是指:用 TCP/IP 技术的叫做因特网,用 HTTP 技术的叫做万维网。

万维网当然也是因特网的一种,但不是每个因特网的应用都使用 HTTP 协议,例如 email 邮件系统使用 SMTP 协议。

curl 指令

curl 指令可以在命令行中发送 HTTP Request:


其中>的部分就是完整的 Request 数据包,<的部分就是完整的 Response 数据包。
要更人性化的显示,可以用httpie工具。(可用brew install httpie安装)

浏览器的 HTTP 工具

TCP/IP 二进制的 Header 不同,HTTP Header 是可以阅读的文字。

Inspector

使用 Google Chrome 的 Inspector 可以观察 HTTP Networking 的情形:



每一行就是一个 HTTP Request。你会发现浏览器要完整加载一个网页,必须要发送非常多的 Request。我们继续观察:



第一个 Request 拿到 HTML,接着浏览器会解析 HTML 内容,找出还需要哪些 CSS/JavaScript 和图片等资源,然后再发送 HTTP Request 去抓取这些资源。

可以进一步观察每一个 Request 的细节,包括 Request HeaderResponse Header

Extension

介绍一个 Google Chrome 扩展插件Postman。它是一个万用的表单工具,可以让我们在浏览器中发送自定义的 HTTP Request。


通过这个工具,我们可以指定 URL 地址、HTTP 方法和要传递的参数。

HTTP 数据包详解

HTTP Request

一个 HTTP Request 包括:

  • HTTP 方法(Method)
  • 网址(包括 URL + parameters)
  • HTTP 首部 (Header)
  • 信息内容 (Message Body)(GET 方法没有这个部分)

举例来说:

> GET / HTTP/1.1 # 第一个是 HTTP 方法;HTTP 目前通行的版本是 HTTP 1.1
> Host: baidu.com # 第二行及之后是 HTTP Header,以 key: value 格式
> User-Agent: curl/7.51.0
> Accept: */*
>
  • Host 指域名。在同一个 IP 对应的服务器上可能同时有多个网站(TCP/IP 首部上的 IP 是一样的数字,抵达服务器后也用一样的 Port (HTTP 用80、HTTPS 用443)),通过 Host 来区分。

  • User-Agent是用户使用的软件。由于历史因素,这些字符串都很丑。另外,User-Agent 不代表客户端真的用这个软件,可自行修改。

  • Accept 指希望服务器回传的格式。*/*是都可以的意思。像 Chrome 浏览器的 GET 操作默认会用text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,表示第一优先是 text/html格式。

  • 如果 HTML 表单用 POST 送出,浏览器则会用 application/x-www-form-urlencodedform-data格式,详见四种常见的 POST 提交数据方式

  • 信息内容(Message Body):GET 没有信息内容,要用POST/PATCH/PUT/DELETE才会有。另外,因为浏览器的网址有长度限制,若 HTTP Request 带很多数据时就不能用 GET。所以 HTML 表单的送出默认是用 POST。

HTTP 方法 常见有以下几种:
  • GET: 用来读取数据 (安全(Safe) 且 幂等(Idempotent))。情形:在浏览器中输入网址;点击网页中的超连结。
  • POST: 用来新增数据或执行某个操作 (不安全(Non-safe) 且 不幂等(non-idempotent))。情形:网页中的 form 表单,默认用 POST 送出。
  • PUT: 用来置换数据 (不安全 但 幂等)
  • PATCH: 用来修改数据 (不安全 且 不幂等)
  • DELETE: 用来删除数据 (不安全 但 幂等)

在 Ajax 中,我们可以用 JavaScript 自行指定要用哪一种 HTTP 方法。

安全 :这个操作不会修改服务器数据。
但这只是语意的意思,不代表服务器的实作。如 GET 某个网页,服务器还是可以实作浏览量(Page view)功能,每次有人浏览就在数据库 +1。但是我们并不会因此就改成用 POST 或 PATCH 方法,因为从用户的立场来说,浏览网页并不预期会修改或新增数据。
幂等 :如果相同操作再执行第二遍第三遍,结果还是跟第一遍的结果一样。例如点 删除 第二次或是第三次,最终结果跟点完第一次是一样的,那笔数据已经被删除了。
这个特性对支持离线(offline)功能的软件比较重要,表示这个操作如果失败,是可以放心重试(retry)的。
不过浏览器默认不支持离线功能,没连上网无法浏览和操作网站。像桌面客户端或手机软件才会优先考虑离线场景。

另外,还有一些细节要注意:
互联网都会假设 GET 是可以重复读取并缓存的,而 POST 不行。因此搜索引擎只会用 GET 抓资料,像这篇文章就闹了笑话,这个人用 GET 来删除资料,造成 Google 爬虫一爬就删除了,他还以为是 Google 故意黑他…

浏览器的表单送出是 POST。如果我们在 action 中不 redirect (也就是让浏览器去 GET 另一页),而是直接 render 返回,那如果用户重新刷新页面的话,浏览器会跳出以下警告:

要求用户确认是否再 POST 一次,这可能会造成重复操作(重复新增)。如果是 GET 的话,刷新就不会有这种警告了。

PUT 和 PATCH 看起来很像,在 Rails 中 PUT 和 PATCH 的确没有差别,但根据 HTTP 规格有语意上的差异,详细可以参考 HTTP Verbs: 谈 POST, PUT 和 PATCH 的应用


HTTP Response

一个 HTTP Response 包括:

  • 状态码 (Response Status)
  • 首部 (Response Headers)
  • 信息内容(Response Body)

举例来说:

< HTTP/1.1 200 OK # HTTP/1.1 - 版本、200 - 状态码数字、OK - 状态码
< Content-Type: text/html # Response Headers : 信息内容格式
<html> # Response Body 信息内容,可以是任何格式,例如 HTML 、 JSON 或是二进制图片等
<h1>这是HTML源码</h1>
<p>....</p>
</html>

状态码数字:2XX(成功,例如 200),3XX(转向,例如 301 (永久转向)、302 (暂时转向)),4XX(客户端问题: 例如 404),5XX(服务器问题: 例如 500)…
完整的 HTTP 状态码列表 看起来很多,一般不会区分这么细。对浏览器来说 200、301、302 就够了。很多状态码的设计是针对网络设备、Web API 使用,而非浏览器场景.

常见的 Request Header 有:

  • Content-Type :指明信息内容格式。 Response Body 是没有限制格式的,如 HTML 是 text/html; harset=UTF-8;在 Web API 中有时用application/json;如果是 JPG 图片则是image/jpeg。完整的内容格式列表请参考 MIME
  • Location :搭配 301 或 302 转向的时候使用。如当浏览器看到 301 状态码时,会用 Location 写的地址来做转址,浏览器会跳去 Location 指定的网址(如果是你自己写的客户端程序,看到 301 或 302 不会自动跳转)。
  • Accept-Encoding: gzip, deflate, br :告诉浏览器信息内容经过压缩,请解压。现代 HTTP 服务器都有这样的功能,可以大大加速下载速度。

现代浏览器还支持一些增加安全性的 Header,如 Strict-Transport-SecurityX-Content-Type-Options: nosniffX-XSS-Protection等,对网络安全有兴趣的请参考:HTTP Headers 的资安议题 (1)Content-Security-Policy - HTTP Headers 的资安议题 (2)HttpOnly - HTTP Headers 的资安议题 (3)

HTTP Header 还有一些是告诉浏览器和路由如何缓存、哪些信息可以缓存,例如 Cache-ControlETagLast-Modified 等,让浏览器不需要重复抓取一样的内容。有兴趣的请参考 HTTP Caching。在正式的部署中,我们也会设定静态文件(CSS/JavaScript/图片等)可以缓存,告诉浏览器可以放心地缓存这些静态文件。( Rails 的 Asset Pipeline 会将静态文件的文件名修改成根据内容加上 digest 编码(只要静态文件内容修改,文件名就会变更),那浏览器就会重新下载到最新的档案,而不怕继续用旧的缓存)


HTTP 的无状态(Stateless)特性,以及用 Cookie 做状态管理

在 HTTP 的设计中,每个 Request 之间都是独立的(和前后 Request 相比),也就是说每个 Request 必须带有完整的参数来完成操作。

那要如何识别已经登入的用户呢? 每个 Request 都要自带可以识别的信息。

如浏览器会使用 Cookies 功能。服务器透过 Response HeaderSet-Cookie 来告诉浏览器要记住一些数据,然后浏览器会在之后的 Request Header 都带有 Cookie 这个 Header



Cookie 只会向同一个域名送出,这是浏览器的安全同源政策 Same-origin policy

截图中还看到除了 baidu 之外的 Cookie,这是因为这个网页安装有其他插件放在 HTML 上。你可以在网页上放其他域名的 JavaScript/CSS或图片。


关于 URL

让我们进一步了解浏览器中的网址 URL(Uniform Resource Locator)是什么。
http://www.example.com/home举例:

Query Strings/Parameters: 可用来额外传递一些参数给服务器,如 http://www.example.com?search=ruby&results=10,其中:

  • ?: 保留符号,query string的开头
  • &: 保留符号,分隔 Parameter Name/Value,如 search=rubyresults=10
  • GETPOST 都可以用 Query Strings来传递参数,但 GET 比较常见(因为 GET Request 也只能用这种方式传参数给服务器 (GET 没有 body))

但是用 Query String 有一些限制:

  • 有长度限制 (因为 URL 长度有限制,太长会被截掉)。因此如果参数真的很多,有时候就算是查询,也会改用 POST 做(进阶查询功能,参数多到 GET 的网址放不下)
  • 不适合传敏感信息。例如帐号密码或信用卡号码等,因为放网址太明显,用户甚至可以加入书签等。
  • 空白和特殊字符必须做encoded(当作网址的参数都必须要经过编码,这有一套规则(URL Encoding),如:
    空白变成 %20
    ! 变成 %21
    + 变成 %2B
    # 变成 %23
    其他还有/`` ? ``: ``@`` &
    这就是为什么有时候网址很长很丑的原因。中文字也需要经过编码 (但现代浏览器会自动做转换,所以中文字看起来可以使用,是因为浏览器帮忙处理编码了)。


HTTPS 和 HTTP/2

近年来由于上网安全意识的提升,越来越多的网站使用 HTTPS 加密链接:



没有加密的 HTTP 链接,通过监听网络封包就可知道浏览器传输的内容。如:

  • 在咖啡厅上网,黑客可以通过监听无线网络,看到你上网的一举一动。
  • 有些咖啡厅可以在网页中植入广告,很容易修改网页内容。

推荐阅读:

HTTP/2 是 2015 年制定的最新标准,语意和功能都与 HTTP 1.1 是相容的,主要是改善加载网页的效能,详细请参考《更快更安全: 每个网站都应该升级到 HTTP/2》


总结

我们总结下一开始的问题:当我们在浏览器输入一个网址到显示出网页,背后发生的事情:


解说:

  1. 在网址列输入网址,或是点网页上的超链接
  2. 浏览器解析这个 URL 找出 protocol(协议)、host、port 和 path
  3. 如果是 HTTP,则组出 HTTP request 数据包
  4. 查询 DNS, 用 host 找出对应的 IP,组出 IP 数据包
  5. 组出 TCP 封包并打开一个 TCP connection 到上述 IP 和 Port
  6. 送出 HTTP Request 数据包
  7. Web 服务器(如 Nginx)接收到 HTTP Request 封包后,解析其中的内容(特别是其中的 path)来决定如何回应:
    *** 如果是静态内容,也就是 HTTP Response 不会根据不同用户、不同时间等任何因素而改变,例如图片、影片、CSS、JavaScript 等。那么 Web 服务器会直接将该档案回传给浏览器。
    *** 如果是动态内容,也就是会根据不同登陆的用户、时间等参数来回传不同内容,那么 Nginx 会将 HTTP Request 交给应用程序处理,如 PHP、Ruby、Python、Node.js、Java 等程序,由该程序动态地根据不同参数和条件,搭配数据库来取出资料,然后组合出 HTML 网页,包成 HTTP Response 经由 Nginx 回传给浏览器。
  8. 浏览器接收到这个 HTTP Response,开始解析这份 HTML 网页成为 Document Object Model (DOM - 一种树状的资料结构来对应 HTML 的标签节点)
  9. 浏览器会依序检查 HTML 源代码中,需要下载的资源网址 URL,如 CSS、图片、JavaScript 等,再发 HTTP Request 请求下载回来 (依照上述步骤 2 到 9 )
    *** 如果下载到 CSS 样式表,就会去装饰对应的 DOM 节点。
    *** 如果下载到 JavaScript ,则会执行它。浏览器上的 JavaScript 程序可以操作 DOM 节点,通常会用 jQuery 来做,来达到一些网页动态特效。
  10. 直到所有资源都下载完毕,浏览器执行完 CSS 和 JavaScript,才算大功告成完成 Page Load。如果你又点击网页上的超连结,则又回到步骤 1 …

拓展阅读

转自 时间的朋友 全栈营

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350