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

2017-12-12  本文已影响25人  时间之友

什么是 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 包括:

举例来说:

> GET / HTTP/1.1 # 第一个是 HTTP 方法;HTTP 目前通行的版本是 HTTP 1.1
> Host: baidu.com # 第二行及之后是 HTTP Header,以 key: value 格式
> User-Agent: curl/7.51.0
> Accept: */*
>
HTTP 方法 常见有以下几种:

在 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 包括:

举例来说:

< 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 有:

现代浏览器还支持一些增加安全性的 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 有一些限制:


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 …

拓展阅读

转自 时间的朋友 全栈营

上一篇 下一篇

猜你喜欢

热点阅读