文章持续更新,能够微信搜一搜「golang小白成长记」第一时间阅读,回复【教程】获golang免费视频教程。本文已经收录在GitHub github.com/xiaobaiTech… , 有大厂面试完整考点和成长路线,欢迎Star。html
HTTP 全称超⽂文本传输协议,也就是HyperText Transfer Protocol。 其中咱们常见的文本,图片,视频这些东西均可以用超文本进行表示,而我常看的猫片,也属于超文本,因此你们不要再说我偷偷看猫片了,我只是在看超文本。HTTP只是定义了一套传输超文本的规则,只要符合了这一套规则,无论你是用iphone,仍是用老爷机,均可以实现猫片的传输。nginx
大概了解了HTTP后,给你们看看它在它们家族里的地位。HTTP位于应用层,跟它相似的协议还有常见的FTP协议,常见的某影天堂的下载连接曾经常常是以FTP开头的。git
有点抽象?不知道小白说的啥?那实操一下,用wireshark
抓包看一下猫片里的请求报文和响应报文具体长什么样子吧github
GET /cmaskboss/164203142_30_1.enhance.webmask HTTP/1.1
Host: upos-sz-staticks3.bilivideo.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
Accept: */*
Origin: https://www.bilibili.com
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://www.bilibili.com/
Accept-Encoding: identity
Accept-Language: zh-CN,zh;q=0.9
Range: bytes=0-16
复制代码
这上面第一行的GET 就是请求方法,/cmaskboss/164203142_30_1.enhance.webmask
则是 URL , 而HTTP/1.1
则是协议版本。接下来从Host
开始到最后一行Range
,都是Headers头。golang
HTTP/1.1 206 Partial Content
Content-Type: application/octet-stream
Content-Length: 17
Connection: keep-alive
Server: Tengine
ETag: "92086de1e6d1d4791fb950a0ac7e30ba"
Date: Sat, 30 Jan 2021 09:31:31 GMT
Last-Modified: Sun, 04 Oct 2020 01:54:28 GMT
Expires: Mon, 01 Mar 2021 09:31:31 GMT
Age: 1018695
Content-Range: bytes 0-16/353225
Accept-Ranges: bytes
X-Application-Context: application
x-kss-request-id: 75bcbfa8ab194e3c825e89c81a912692
x-kss-BucketOwner: MjAwMDAyMDEwNw==
X-Info-StorageClass: -
Content-MD5: kght4ebR1HkfuVCgrH4wug==
X-Cache-Status: HIT from KS-CLOUD-JH-MP-01-03
X-Cache-Status: HIT from KS-CLOUD-TJ-UN-14-13
X-Cache-Status: HIT from KS-CLOUD-LF-UN-11-25
Access-Control-Allow-Origin: https://www.bilibili.com
Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,range
X-Cdn-Request-ID: 7e2c783ca7d392624118593ec1dc66bc
复制代码
相似请求报文,HTTP/1.1
是协议版本,206
是状态码,Partial Content
则是状态描述符。接下来从Content-Type
开始到最后一行X-Cdn-Request-ID
都是Headers信息。web
其实上面的抓包信息,在浏览器里按F12就能看到,之因此要用wireshark
可能只是装X效果比较好吧。按下F12看到的响应数据就跟下图展现的那样。面试
URL 表明着是统一资源定位符(Uniform Resource Locator)。做用是为了告诉使用者 某个资源在 Web 上的地址。这个资源能够是一个 HTML 页面,一个 CSS 文档,一幅图像或一个猫片等等。上面咱们请求猫片的URL就是 https://upos-sz-staticks3.bilivideo.com/cmaskboss/164203142_30_1.enhance.webmask
这里面细分,又能够分为好几个部分。shell
表示该URL的协议部分为http仍是https,会用//为分隔符。上面的URL表示网页用的是HTTPS协议,而上面提到的X影天堂用的则是ftp协议的下载连接。json
域名是upos-sz-staticks3.bilivideo.com
,在发送请求前,会向DNS服务器解析IP,若是已经知道ip,还能够跳过DNS解析那一步,直接把IP当作域名部分使用。后端
域名后面有些时候会带有端口,和域名之间用:分隔,端口不是一个URL的必须的部分。当网址为http://时,默认端口为80
当网址为https://时,默认端口为443,以上两种均可以省略端口号。上面的URL其实省略了443端口号。
从域名的第一个/开始到最后一个/为止,是虚拟目录的部分。虚拟目录也不是URL必须的部分,本例中的虚拟目录是/cmaskboss/
从域名最后一个/开始到?为止,是文件名部分;若是没有?,则是从域名最后一个/开始到#为止,是文件名部分;若是没有?和#,那么就从域名的最后一个/从开始到结束,都是文件名部分。本例中的文件名是164203142_30_1.enhance.webmask
,文件名也不是一个URL的必须部分。
其实一直有个误解,不少人觉得URI是URL的子集,其实应该反过来。URL是URI的子集才对。简单解释下。 假设"小白"(URI)是一种资源,而"在迪丽亦巴的怀里"代表了一个位置。若是你想要找到(locate)小白,那么你能够到"在迪丽亦巴怀里"找到小白,而"在迪丽亦巴怀里的/小白"才是咱们常说的URL。而"在迪丽亦巴怀里的/小白"(URL)显然是"小白"(URI)的子集,毕竟,"小白"还多是"在牛亦菲怀里的/小白"(其余URL)。
HTTP 定义了一组请求方法,以代表要对给定资源执行的操做。指示针对给定资源要执行的指望动做.。虽然他们也能够是名词,但这些请求方法有时被称为HTTP动词.。每个请求方法都实现了不一样的语义。
此次请求B站猫片的请求里用的是GET,意味着获取。但其实HTTP定义了多种请求方法,来知足各类需求。除了Get,还有几个POST、HEAD、OPTIONS、PUT、DELETE、TRACE 和 CONNECT。
常见的各个请求方法的具体功能以下:
请求指定的页面信息,并返回消息主体(body)+头信息(header)。
HEAD和GET本质是同样的,区别在于HEAD只返回头信息(header),不返回消息主体(body)。你们不要觉得它没用,它跟GET和POST同样,在http/1.0的时候就存在了,实属三元老之一了。主要用途
若是想要判断某个资源是否存在,虽然用GET也能作到,但这里用HEAD还省下拿body的消耗,返回状态码200就是有404就是无
若是请求的是一个比较大的资源,好比一个超大视频和文件,你只想知道它到底有多大,而不须要整个下载下来,这时候使用HEAD请求,返回的headers会带有文件的大小(content-lenght
)。
向服务器提交数据。这个方法用途普遍,几乎目前全部的提交操做都是靠这个完成。POST跟GET最经常使用,但最大的区别在于,POST每次调用均可能会修改数据,是非幂等的,而GET相似于只读,是幂等的。
这个方法比较少见。在HTTP规范中POST是非等幂的,屡次调用会产生不一样的结果。好比:建立一个用户,因为网络缘由或是其余缘由多建立了几回,那么将会有多个用户被建立。而PUT id/xiaobai 则会建立一个id为 xiaobai 的用户,屡次调用仍是会建立的结果是同样的,因此PUT是等幂的。可是通常为了不形成心智负担,实战中也会使用POST替代PUT。
删除某一个资源。基本上这个也不多见,通常实战中若是是删除操做,也是使用POST来替代。
它用于获取当前URL所支持的方法。若请求成功,则它会在HTTP响应头部中带上给各类“Allow”的头,代表某个请求在对应的服务器中都支持哪一种请求方法。好比下图:
这里面须要关注的点有两个
Request Header里的关键字段
Response Header里的关键字段
Options
堪称是网络协议中的老实人,就好像老实人刚谈了个女友,每次牵手前都要问下人家 “我能够牵你的手吗?”, “我能够抱你吗?”,获得了答应后才会下手。差点被这老实人气质感动得留下了不争气的泪水。
在跨域(记住这个词,待会解释)的状况下,浏览器发起复杂请求前会自动发起 options 请求。跨域共享标准规范要求,对那些可能对服务器数据产生反作用的 HTTP 请求方法(特别是 GET 之外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 options 方法发起一个预检请求,从而获知服务端是否容许该跨域请求。服务器确认容许以后,才发起实际的 HTTP 请求。
这里提到了两个关键词:
某些请求不会触发 CORS 预检请求,这样的请求通常称为"简单请求",而会触发预检的请求则为"复杂请求"。
简单请求
GET、HEAD、POST
Headers
字段
Accept
Accept-Language
Content-Language
Content-Type
DPR/Downlink/Save-Data/Viewport-Width/Width
(这些不常见,放在一块儿)Content-Type
只有如下三种
application/x-www-form-urlencoded
multipart/form-data
text/plain
复杂请求
因而可知,由于上述请求在获取B站资源的请求Headers里带有 Access-Control-Request-Headers: range
, 而range
正好不在简单请求的条件2中提到的Headers范围里,所以属于复杂请求,因而触发预检options请求。
刚刚提到了一个词叫跨域,那什么是跨域呢?在了解跨域以前,首先要了解一个概念:同源。所谓同源是指,域名、协议、端口均相同。
不明白不要紧,举个例子。
须要特别注意的是,localhost和127.0.0.1虽然都指向本机,但也不属于同源。
而非同源之间网页调用就是咱们所说的跨域。在浏览器同源策略限制下,向不一样源发送XHR请求,浏览器认为该请求不受信任,禁止请求,具体表现为请求后不正常响应。
因而可知,复杂请求的条件其实很是容易知足,而一旦知足复杂请求的条件,则浏览器便会发送2次请求(一次预检options,一次复杂请求),这一次options就一来一回(一个RTT),显然会致使延迟和没必要要的网络资源浪费,高并发状况下则可能为服务器带来严重的性能消耗。
每次复杂请求前都会调用一次options,这其实很是没有必要。由于大部分时候相同的请求,短期内得到的结果是不会变的,是否能够经过浏览器缓存省掉这一次查询?
Access-Control-Max-Age
就是优化这个流程中使用的一个Header。它的做用是当你每次请求options
方法时,服务端返回调用支持的方法(Access-Control-Allow-Methods )和Headers(Access-Control-Allow-Headers)有哪些,同时告诉你,它在接下来 Access-Control-Max-Age
时间(单位是秒)里都支持,则这段时间内,再也不须要使用options进行请求。特别注意的是,当Access-Control-Max-Age
的值为-1时,表示禁用缓存,每一次请求都须要发送预检请求,即用OPTIONS请求进行检测。
HTTP Status Code是常说的HTTP状态码。当用户访问一个网页时,浏览器会向网页所在服务器发出请求。服务器则会根据请求做出响应,而状态码则是响应的一部分,表明着本次请求的结果。全部状态码的第一个数字表明了响应的大概含义,组合上第二第三个数字则能够表示更具体的缘由。若是请求失败了,经过这个状态码,大概初步判断出此次请求失败的缘由。如下是五类状态码的含义。
能够根据如下流程图了解下各种状态码间的关系。
这是最多见的状态码。表明请求已成功,数据也正常返回。而B站猫片里虽然响应成功了,但却不是200,而是206,是为何呢,接下去继续看看。
这个状态码在上面B站请求的响应结果。服务器已经成功处理了部分 GET 请求。相似于B站看视频或者迅雷这类的 HTTP下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。
307 Temporary Redirect
内部重定向。重定向的意思是,当你输入一个网址的时候,浏览器会自动帮你跳转到另一个网址上。好比,当你在浏览器输入框输入http://www.baidu.com/
时。因为使用http并不安全,百度会自动帮你跳转到它对应的https网页上。而此时,须要重定向的地址,会经过Response Headers
的Location
返回
404 Not Found
请求失败,请求所但愿获得的资源未被在服务器上发现。出现这个错误的最有可能的缘由是服务器端没有这个页面,或者是Request Method与注册URL的Method不一致,好比我有一个URL在服务端注册的Request Method 为 POST,但调用的时候却错误用了GET,则也会出现404错误。
499 Client has closed connection
网络请求过程当中,因为服务端处理时间过长,客户端超时。通常常见于,后端服务器处理时间过长,而客户端也设置了一个超时等待时间,客户端等得“不耐烦”了,主动关掉链接时报出。
502 Bad Gateway
服务器方面没法给予正常的响应。通常常见于服务器崩溃后,nginx 没法正常收到服务端的响应,给客户端返回502状态码。
504 Gateway Timeout
网络请求过程当中,因为服务端处理时间过长,网关超时。通常常见于,后端服务器逻辑处理时间过长,甚至长于 nginx设置的最长等待时间时报错。它跟 499 状态码很是像,区别在于499 表示的是客户端超时,504是网关超时。若是是499超时,能够考虑修改客户端的代码调整超时时间,若是是504,则考虑调整nginx的超时配置。
Content-Length
是HTTP的消息长度, 用十进制数字表示。Content-Length
首部指出报文中消息的当前实际字节大小。若是消息文本进行了gzip压缩的话, Content-Length
指的就是压缩后的大小而不是原始大小。
正常状况下Content-Length
是不须要手动去设置的,大部分语言的网络库都会自动封装好,可是若是在一些特殊状况下,出现Content-Length
与实际要发送的消息大小不一致,就会出现一些问题。
若是Content-Length
< 实际长度
下面启动一个HTTP服务器,全部语言都同样,示例里使用了golang。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
// w表示response对象,返回给客户端的内容都在对象里处理
// r表示客户端请求对象,包含了请求头,请求参数等等
func index(w http.ResponseWriter, r *http.Request) {
b, _ := ioutil.ReadAll(r.Body)
fmt.Printf("request body=%#v, content_length=%v \nheaders=%v",string(b), r.ContentLength, r.Header)
// 往w里写入内容,就会在浏览器里输出
fmt.Fprintf(w, string(b))
}
func main() {
// 设置路由,若是访问/,则调用index方法
http.HandleFunc("/", index)
// 启动web服务,监听9090端口
err := http.ListenAndServe(":9999", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
复制代码
在控制台输入
$ $ curl -L -X POST 'http://127.0.0.1:9999' -H 'Content-Type: application/json' -H 'Content-Length: 5' -d '1234567' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 12 100 5 100 7 828 1160 --:--:-- --:--:-- --:--:-- 1400
12345
复制代码
输入的body是 1234567
,共7个数字,可是输入的 Content-Length
为 5。到了服务器那,收到了 12345
,共5个数字,数量上跟输入的Content-Length
一致。 因而可知当Content-Length
< 实际长度, 消息会被截断。
若是Content-Length
> 实际长度
仍是上面的服务端代码,可是控制台输入如下命令
$ curl -L -X POST 'http://127.0.0.1:9999' -H 'Content-Type: application/json' -H 'Content-Length: 100' -d '1234567' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 7 0 0 0 7 0 0 --:--:-- 0:01:19 --:--:-- 0
复制代码
此次状况不太同样,会发现请求一直阻塞没有返回。这是由于输入的body是 1234567
,共7个数字,可是输入的 Content-Length
为 100。也就是服务端一直认为此次的body长度为100,可是目前只收到了部分消息(长度为7),剩余的长度为93的消息因为各类缘由还在路上,所以选择傻傻等待剩下的消息,就形成了上面提到的阻塞。
视频播放须要支持用户调整播放进度,支持让用户选择直接跳到中间部分开始播放。为了实现这个功能,须要经过HTTP Range Requests 协议用于指定须要获取视频片断。而 Request Header里的range头则是用于指定要请求文件的起始和结束位置。
Range: bytes=0-
的请求,向服务器请求从开始到结尾的完整文件。Content-Range:开始字节位置-结束字节位置/文件大小(byte)
。Connection: close
表示请求响应完成以后当即关闭链接,这是HTTP/1.0请求的默认值。每次请求都通过“建立tcp链接 -> 请求资源 -> 响应资源 -> 释放链接”这样的过程
Connection: keep-alive
表示链接不当即关闭,能够继续响应下一个请求。HTTP/1.1的请求默认使用一个持久链接。能够作到只创建一次链接,屡次资源请求都复用该链接,完成后关闭。流程上是 创建tcp链接 -> 请求资源 -> 响应资源 -> ... (保持链接)... -> 第n次请求资源 -> 第n次响应资源 -> 释放链接。
在http1.1中Request Header和Reponse Header中都有可能出现一个Connection: keep-alive 头信息。Request Header里的Connection: keep-alive 头是为了告诉服务端,客户端想要以长链接形式进行通讯。而Response Header里的Connection: keep-alive 头是服务端告诉客户端,个人服务器支持以长链接的方式进行通讯。若是不能使用长链接,会返回 Connection: close ,至关于告诉客户端“我不支持长链接,你死了这条心,老老实实用短链接吧” 。
咱们知道 HTTP 创建在 TCP 传输层协议之上,而 TCP 的创建须要三次握手,关闭须要四次挥手,这些步骤都须要时间,带给 HTTP 的就是请求响应时延。若是使用短链接,那么每次数据传输都须要经历一次上面提到的几个步骤,若是能只链接一次,保持住这个链接不断开,期间通讯就能够省下创建链接和断开链接的过程,对于提高HTTP性能有很大的帮助。
Cookie 是浏览器访问服务器后,服务器传给浏览器的一段数据。里面通常带有该浏览器的身份信息。
浏览器须要保存这段数据,不得轻易删除。
此后每次浏览器访问该服务器,都必须带上这段数据。服务器用使用这段数据确认浏览器身份信息。
Cookie 通常有两个做用。
识别用户身份。
持久化用户信息。
Referrer 是HTTP请求header的报文头,用于指明当前流量的来源参考页面,常被用于分析用户来源等信息。经过这个信息,咱们能够知道访客是怎么来到当前页面的。好比在上面的请求截图里,能够看出我是使用https://www.bilibili.com/
访问的视频资源。
Referrer 字段,会用来指定该请求是从哪一个页面跳转页来的,里面的信息是浏览器填的。
而 Referrer Policy 则是用于控制Referrer信息传不传、传哪些信息、在何时传的策略。
为何要这么麻烦呢?由于有些网站一些用户敏感信息,好比 sessionid 或是 token 放在地址栏里,若是当作Referrer字段所有传递的话,那第三方网站就会拿到这些信息,会有必定的安全隐患。因此就有了 Referrer Policy,用于过滤 Referrer 报头内容。
好比在上面的请求截图里,能够看出我是使用strict-origin-when-cross-origin
策略,含义是跨域时将当前页面URL过滤掉参数及路径部分,仅将协议、域名和端口(若是有的话)看成 Referrer。不然 Referrer 仍是传递当前页的全路径。同时当发生降级(好比从 https:// 跳转到 http:// )时,不传递 Referrer 报头。
cache-control,用于控制浏览器缓存。简而言之,当某人访问网站时,其浏览器将在本地保存某些资源,例如图像和网站数据。当该用户从新访问同一网站时,缓存控制设置的规则会肯定该用户是否从本地缓存中加载这些资源,或者浏览器是否必须向服务器发送新资源的请求。
浏览器缓存是指浏览器本地保存网站资源,以便没必要再次经过网络从服务器获取它们。例如,“猫猫网”的背景图像能够保存到本地缓存中,这样在用户第二次访问该页面时,该图像将从用户的本地文件加载,剩下网络获取资源的时间,页面加载速度就会更快。
可是浏览器也不会永远把这些网站资源放在本地,不然本地磁盘就会炸,因此会限定保存资源的时间,这叫生存时间(TTL)。若是 TTL 过时后用户请求缓存的资源,浏览器必须再次经过网络与服务器创建链接并从新下载这个资源。
cache-control: private 具备“private”指令的响应只能由客户端缓存,不能由中间代理(例如 CDN或代理)缓存。这些资源一般是包含私密数据的资源,例如显示用户我的信息的网站。
cache-control: public 相反,“public”指令表示资源能够由任何缓存存储。
cache-control: no-store 带有“no-store”指令的响应没法缓存到任何位置,也永不缓存。也就是说,用户每次请求此数据时,都必须将请求发送到源站服务器以获取新副本。此指令一般保留给包含极其敏感数据的资源,例如银行账户信息。
cache-control: max-age 此指令指定了生存时间,也就是资源在下载后能够缓存多少秒钟。例如,若是将最大期限设置为 1800,则首次从服务器请求资源后的 1800 秒(30 分钟)内,后续请求都会向用户提供该资源的缓存版本。若是 30 分钟后用户再次请求资源,则客户端须要向服务器从新请求该资源。
cache-control: no-cache
从B站截图里能够看出,使用的缓存控制指令是cache-control: no-cache
。它表示,只有先检查资源没有更新版本后,才可以使用所请求资源的缓存版本。那么问题来了,怎么判断资源是否有更新版本呢?这就须要 ETag
。
Etag是 Entity tag的缩写,是服务端的一个资源版本的令牌标识。在 HTTP 响应头中将其传送到客户端。每当资源更新时,此令牌会更新。
好比,浏览器第一次请求资源的时候,服务端返回了这个资源的ETag: "095933fff2323351d3b495f2f879616f1762f752"
。
当浏览器再次请求这个资源的时候,浏览器会将If-None-Match: "095933fff2323351d3b495f2f879616f1762f752"
传输给服务端,服务端拿到该ETAG,对比资源是否发生变化。
此过程可确保用户始终得到资源的最新版本,而且无需进行没必要要的下载。
果真B站是个充满学习氛围的地方,看个猫片都能学到这么多硬核知识。接下来我打算去舞蹈区看看有没有适合大家的知识点。
我是小白,有空?一块儿在知识的海洋里呛水啊,懂我意思?
- [1] 计算机网络自动向下
- [2] 极客时间-趣谈网络协议
- [3] 极客时间-透视HTTP
- [4] 图解HTTP
- [5] 漫画形象-小肥柴