HTTP/0.9
HTTP 于 1990 年问世。那时的 HTTP 并没有作为正式的标准被建立。现在的 HTTP 其实含有 HTTP1.0 之前 版本的意思,因此被称为
HTTP/0.9。
该版本只有一个命令GET,没有header等描述数据的信息,服务器只能回应HTML格式的字符串,不能回应别的格式。服务器发送完毕,就关闭tcp连接。
HTTP/1.0
HTTP 正式作为标准被公布是在 1996 年的 5 月,版本被命名为 HTTP/1.0,并记载于 RFC1945。
首先,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。
其次,除了GET命令,还引入了POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。
再次,HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part
type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。
HTTP/1.0
版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow
start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。`Connection:
keep-
alive`一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。
HTTP/1.1
1997 年 1 月公布的 HTTP/1.1, 比1.0版本晚了半年,它进一步完善了HTTP协议,是目前主流的 HTTP 协议版本。
1.1
版增加了请求命令(OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECTION。客户端请求的头信息新增了Host字段,用来指定服务器的域名。
1.1 版的最大变化,就是引入了持久连接(persistent
connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive
。
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection:
close,明确要求服务器关闭TCP连接。
目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。
缺点:
虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为队头堵塞
。
为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain
sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。
HTTP/2.0
HTTP/2.0版本中所有数据以二进制(最小数据单位是帧)传输,HTTP/1.1中大部分数据通过字符串的形式;同一个连接里面发送多个请求不再需要按照顺序来;头信息压缩以及推送等提高效率的功能。
HTTP/2.0 正在制订中,但要达到 较高的使用覆盖率,仍需假以时日。
五层模型_.pic.jpg
用户通常使用主机名称或者域名来访问对方的计算机,因为更好记住。但是计算机更擅长处理IP地址这样的一串数字。DNS服务就是为了解决这个问题,DNS协议通过域名查找IP地址,或者逆向从IP地址查询域名的服务。
DNS服务的作用.png
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
建立起一个TCP连接需要经过三次握手
, 关闭TCP连接需要经过四次挥手
。
三次握手.png
1)发送端首先发送一个带有SYN(synchronize)标志地数据包给接收方。
2)接收方接收后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了。
3)最后,发送方再回传一个带有ACK标志的数据包,代表我知道了,表示’握手‘结束。
通俗的说法
1)Client:嘿,李四,是我,听到了吗?
2)Server:我听到了,你能听到我的吗?
3)Client:好的,我们互相都能听到对方的话,我们的通信可以开始了。
其中 位码即tcp标志位,有6种标示:
注:为什么是三次握手
因为网络传输有延迟,客户端发送请求到服务器端要求即那里连接,如果服务器直接返回的话可能会产生丢包的情况导致客户端收不到数据,客户端会因为超时就关闭了,可能就去发送新的请求了,然而服务器端并不知道丢包导致客户端没有接收数据。服务器端t端口就一直开着,造成额外的开销。所以需要三次握手确认这个过程。
四次挥手.png
1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手
通俗的说法
1)Client:我所有东西都说完了。
2)Server:我已经全部听到了,但是等等我,我还没说完。
3)Server:好了,我已经说完了。
4)Client:好的,那我们的通信结束。
注:TCP为什么要进行四次挥手
4次挥手的目的是终止数据传输,并回收资源,此时两个端点两个方向的序列号已经没有了任何关系,必须等待两方向都没有数据传输时才能拆除虚链路,不像初始化时那么简单,发现SYN标志就初始化一个序列号并确认SYN的序列号。因此必须单独分别在一个方向上终止该方向的数据传输。
一次HTTP操作称为一个事务,其工作整个过程如下:
1 ) 地址解析如用客户端浏览器请求这个页面:http://localhost.com:8080/index.html
从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下:
协议名:http
主机名:localhost.com
端口:8080
对象路径:/index.htm
在这一步,需要域名系统DNS解析域名localhost.com,得主机的IP地址。
2)封装HTTP请求数据包
把以上部分结合本机自己的信息,封装成一个HTTP请求数据包
3)封装成TCP包,建立TCP连接(TCP的三次握手)
在HTTP工作开始之前,客户机(Web浏览器)首先要通过网络与服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。这里是8080端口
4)客户机发送请求命令
建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URI:Uniform Resource
Identifier)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
5)服务器响应
服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
实体消息是服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此结束,接着,它就以Content-
Type应答头信息所描述的格式发送用户所请求的实际数据
6)服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码`Connection:keep-
alive`
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
经典面试题【从输入URL到页面加载发生了什么?】
报文(message)
报文是网络中交换与传输的数据单元,也是网络传输的单元。报文包含了将要发送的完整的数据信息,其长短不需一致。报文在传输过程中会不断地封装成分组、包、帧来传输,封装的方式就是添加一些控制信息组成的首部,那些就是报文头。
http有两种报文
请求行 (HTTP请求报文的第一行)
请求行由方法字段、URL字段和HTTP协议版本字段。其中,方法字段严格区分大小写,当前HTTP协议中的方法都是大写。
状态行 HTTP响应报文的第一行
状态行包括三个字段:协议版本、状态码与原因短语
1. 什么是跨域
浏览器的同源策略限制了跨域请求资源。即当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
2. 实现跨域的常用方法
(1) jsonp (JSON+Padding)将JSON数据填充进回调函数
jsonp 是解决跨域问题的一种方案,不同于 json,其并不是一种数据交换格式,而只是一种绕过跨域的技巧。
简单来说就是创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
因为在HTML标签里,一些标签比如script、img、iframe这样的拥有src属性获取资源的标签是没有跨域限制的。因此利用script加载预设的
callback 将内容传递给 js。一般来说我们约定通过一个参数来告诉服务器 JSONP 返回时应该调用的回调函数名,然后拼接出对应的
js。以下为一个简单例子。
html页面
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript">
var infoHandler = function(data){
console.log(data);
};
</script>
<script type="text/javascript" src="http://a.com/info?name=a&callback=infoHandler"></script>
</head>
<body></body>
</html>
服务器端返回
# 服务器端通过获取请求参数重的callback名称, 拼出返回值。
infoHandler({
"name": "shirley",
"age":18,
"gender": "female",
"idCard": "23213123231232ui323"
});
缺点是JSONP只能发GET请求,因为本质上script加载资源就是GET请求
(2) CORS
CORS:是一个W3C标准, Cross-origin resource sharing 即
“跨域资源共享”。它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
CORS需要客户端和服务端同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
简单请求
(simple request)和非简单请求
(not-so-simple request)。undefined简单请求: 请求满足以下两个条件的就是简单请求undefined1. 请求方法是以下三种方法之一:HEAD,GET,POST 。undefined2. HTTP的头信息不超出以下几种字段undefinedAcceptundefinedAccept-LanguageundefinedContent-LanguageundefinedLast-Event-IDundefinedContent-type:只限于三个值application/x-www-form-urlencoded、multipart/form-
data、text/plainundefined非简单请求: 不满足上面两个条件的就是非简单请求。undefined注意undefined1、对于简单请求,浏览器直接请求,会在请求头信息中,增加一个origin字段,来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值,来决定是否同意该请求,服务器返回的响应会多几个头信息字段。undefined2、非简单请求是对那种对服务器有特殊要求的请求,都会在正式通信之前,增加一次HTTP请求,称之为预检(options请求)。浏览器会先询问服务器,当前网页所在域名是否在服务器的许可名单之中,服务器允许之后,浏览器会发出正式的XMLHttpRequest请求,否则会报错。undefinedoptions请求 : options请求是浏览器自发起的preflight
request(预检请求),获取响应后发现可以跨域,接着就发送真实的请求。1、设置Access-Control-Allow-Origin
'Access-Control-Allow-Origin': 'http://XXX.com' // 设置接指定地址的请求或设置为'*' 表示接受任意域名的请求
'Access-Control-Allow-Methods': 'POST,PUT,HEAD'
'Access-Control-Allow-Headers': 'X-Test-Cors', //设置自定义的请求头
设置缓存, 允许浏览器在指定时间内,无需再发送预检请求,直接用本次结果即可。
'Access-Control-Max-Age': '5', //秒为单位 -1是不缓存 5秒一下chrome都默认是5
设置是否可以携带cookie
'Access-Control-Allow-Credentials': true,//如果Access-Control-Allow-Origin字段设置* 此字段设为true无效
(3) nginx反向代理
原理是:同源策略只是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
// Nginx配置如下:
server{
# 监听8001端口
listen 8001;
# 域名是localhost
server_name localhost;
#凡是localhost:8001/api这个样子的,都转发到真正的服务端地址http://localhost:9001
location ^~ /api {
proxy_pass http://localhost:9001;
}
上面的HTTP报文介绍了首部字段。首部字段同时存在于请求和响应报文内,并涵盖 HTTP
报文相关的内容信息。使用首部字段是为了给客服端和服务器端提供报文主体大小、所使用的语言、认证信息等内容。
通过指定首部字段 Cache-Control 的指令,就能操作缓存的工作机制。指令的参数是可选的,多个指令之间通过“,”分隔。比如: Cache-
Control: private, max-age=0, no-cache。
缓存请求指令.png
缓存响应指令.png
Pragma和Expires也与缓存相关,Pragma是旧产物,已经逐步抛弃,有些网站为了向下兼容还保留了这两个字段。优先级从高到低是:Pragma ->
Cache-Control -> Expires
Cache-Control除了在响应中使用,在请求中也可以使用。我们用开发者工具来模拟下请求时带上Cache-Control:勾选Disable
cache,刷新页面,可以看到Request Headers中有个字段Cache-Control: no-cache。
Connection 首部字段具备以下两个作用:
747969-20170217214935269-1562584482.png
Date 表明创建 HTTP 报文的日期和时间
Accept: text/html, application/xhtml+xml, application/xml; q=0.5
Accept-Encoding: gzip, deflate
Accept-Encoding 首部字段用来告知服务器用户代理支持的内容编码及内容编码的优先顺序,并可一次性指定多种内容编码。同样使用 q=数值
来表示相对优先级。也可使用星号(*)作为通配符,指定任意的编码格式。
Accept-Lanuage: zh-cn,zh;q=0.7,en=us,en;q=0.3
告知服务器用户代理能够处理的自然语言集(指中文或英文等),以及自然语言集的相对优先级,可一次性指定多种自然语言集。同样使用 q=数值 来表示相对优先级。
Host: localhost:8001
首部字段 Host 会告知服务器,请求的资源所处的互联网主机名和端口号。Host 首部字段在 HTTP/1.1
规范内是唯一一个必须被包含在请求内的首部字段。
首部字段 Host 和以单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联,这是首部字段 Host 必须存在的意义
`User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36`
Allow: GET, HEAD
Content-Encoding: gzip
Content-Language: zh-CN
首部字段 Content-Language 会告知客户端,实体主体使用的自然语言(指中文或英文等语言)。
Content-Type: text/html; charset=UTF-8
首部字段 Content-Type 说明了实体主体内对象的媒体类型。和首部字段 Accept 一样,字段值用 type/subtype 形式赋值。参数
charset 使用 iso-8859-1 或 euc-jp 等字符集进行赋值。
Expires: Mon, 10 Jul 2017 15:50:06 GMT
服务器在响应头中用Set-
Cookie头将Cookie的内容回送给客户端,客户端在新的请求中将相同的内容携带在cookie头中发送给服务器,从而实现会话的保持。
setCookie: name=aaa; expires=Mon, 10 Jul 2017 15:50:06 GMT; path=/
undefined属性 说明undefinedNAME=VALUE 赋予 Cookie 的名称和其值(必需项)undefinedexpires=DATE Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止)undefinedpath=PATH 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录)undefineddomain=域名 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie的服务器的域名)undefinedSecure 仅在 HTTPS 安全通信时才会发送 CookieundefinedHttpOnly 加以限制,使 Cookie 不能被 JavaScript 脚本访问首部字段 Cookie 会告知服务器,当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。接收到多个 Cookie
时,同样可以以多个 Cookie 形式发送。
实质是建立报名单制度,明确的告诉客户端,哪些外部资源可以加载和执行。所以使用 CSP 来防止 XSS攻击(跨域脚本攻击)。
Content-Security-Policy: 'default-src \'self\'
表示只能加载同域下资源。
Content-Security-Policy: 'default-src \'self\'; report-uri /report'
表示只能加载同域下资源, 且会发送一个违例报告。默认情况下,违规报告并不会发送。为启用发送违规报告,你需要指定 report-
uri策略指令,并提供至少一个URI地址去递交报告。
如果我只想收集报告,但是不真正的去限制请求,那怎么办?除了Content-Security-Policy,还有一个`Content-Security-
Policy-Report-Only`字段,表示不执行限选项,只是记录违反限制的行为。将头部改为这个即可。
注 : default-src
设置的是全局,如果我只想限制js的请求,可以将default-src改为script-src,
限制图片请求可用img-src
使用meta标签达到一样的效果。
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
详细用法可以参考[Content Security
Policy](https://links.jianshu.com/go?to=https%3A%2F%2Fwww.ruanyifeng.com%2Fblog%2F2016%2F09%2Fcsp.html)
简单来说,就是网络请求的元数据描述,服务端根据这些补充数据进行细粒度的控制响应。浏览器自动带上的请求头,都是Forbidden
header,也就是不能被篡改的。
Sec-Fetch-Dest
: 期望需要什么样的资源,比如 script。
Sec-Fetch-Mode
: 表明了一个请求的模式,比如是否跨域。
Sec-Fetch-Site
: 请求发起者的来源与目标资源来源之间的关系,比如跨域就为 cross-site
。
HTTP 首部字段是可以自行扩展的。所以在 Web 服务器和浏览器的应用上,会出现各种非标准的首部字段。
例如: X-Frame-Options
: 用于控制网站内容在其他 Web 网站的 iframe 标签内的显示问题。
持久连接.png
那是不是只需要建立一个TCP连接,然后发送多个HTTP请求就可以了? 不行。
HTTP/1.1 存在一个问题:单个 TCP
连接在同一时刻只能处理一个请求。如果一个网页上有多个资源需要请求,肯定不能只开一个TCP连接,然后按请求顺序下载,那样用户肯定等的很难受。
所以Chrome最多允许对同一个 Host 建立六个 TCP
连接。(不同的浏览器允许的连接数不同。就解释了之前我们所了解到的的浏览器请求的最大并发量是6个(以chrome为例)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。