深入理解HTTP协议,巩固HTTP知识体系——长文,可收藏阅读

4,917 阅读1小时+

前言

如果你是中高级的前端工程师,相关HTTP问题,在面试的时候被问到的概率很高,且我们项目中会大量遇到相关的问题。

懒人指南:

如果你只是想快速的知道下边面试官经常问的问题的答案,可以略过正式部分(本文为HTTP知识详解部分,其中大量阅读书籍《图解HTTP》收获,和其他网上资料的总结),直接查看问题回答总结部分。

问题:

如果面试官问你以下问题,你是否能答过来,能答几道呢?这也是常见的面试题,来试一试吧~

  • 什么是三次握手?

  • 为什么连接的时候是三次握手,关闭的时候却是四次握手?

  • TCP与UDP的区别?

  • 从输入URL到页面加载完成,发生了什么?

  • HTTP响应码你都知道哪些?都是什么意思?

  • HTTP协议的工作流程?

  • HTTP/1.0 和 1.1 现存的哪些问题

  • HTTP与HTTPS区别

  • 什么是长链接,为什么需要长连接?

  • HTTP/2的信道复用又为什么能提高性能?

  • HTTP的缓存机制

  • XSS和Crsf攻击都有哪些防范手段?

  • 如何高效利用缓存,上线前端代码?

    1、缓存时间过长,发布上线了,用户端还用缓存,会有bug

    2、缓存时间过短,重复加载文件过多,浪费带宽

其实上边的一些问题,在三面、四面的时候问前端性能优化(文件获取优化)的时候也是这些问题。

正式

在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP 。 HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。 HTTP协议属于应用层,建立在传输层协议TCP之上。客户端通过与服务器需要建立TCP连接,之后发送HTTP请求与接收HTTP响应都是通过访问Socket接口来调用TCP协议实现。 因为HTTP是不存在连接这个概念的,只有请求和响应,它们都是数据包。

网络基础及web

web简述

web:(网页浏览器web browser);

web页面不会凭空显示出来。根据Web浏览器地址栏中指定的URL,web浏览器从Web服务器获取资源等信息,从而显示出web页面。web使用HTTP(超文本传输协议)的协议作为规范,完成从客服端到服务器端等一系列运作流程。Web是简历在HTTP协议上的通信。

协议:指定规则的约定;为了让计算机能够通信,计算机需要定义通信规则,这些规则就是协议;是数据封装格式+传输 ;协议有多种;

客户端:像这种通过发送请求获取服务资源的web浏览器等,都可以称为客户端(client)。

网络基础TCP/IP

为了了解HTTP,我们必须先了解TCP/IP协议族因通常使用的网络(包括互联网)是在TCP/IP协议族的基础上运作的。而HTTP属于它内部的一个子集。

TCP/IP

把与互联网相关联的协议集合起来总成为TCP/IP。

也有认为:TCP/IP是指TCP和IP这两种协议。

还有认为:TCP/IP是IP协议的通信过程中,使用到的协议族的统称。

为什么分层?

  • 将复杂的流程分解为几个功能相对单一的子进程
  • 整个流程更加清晰,复杂问题简单化
  • 更容易发现问题并针对性的解决问题

OSI七层网络模型

分层 功能 作用
应用层 网络服务于最终用户的一个接口 提供网络与用户应用软件之间的接口服务,屏蔽了网络传输相关细节
表示层 数据的表示、安全、压缩 提供格式化的表示和转换数据服务,如加密和压缩
会话层 建立、管理、中止会话 提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制
传输层 定义传输数据的协议端口号,以及留空和差错校验 1、提供建立、维护和取消传输连接功能,负责可靠地传输数据(PC);2、向用户提供可靠的(端到端end-to-end)服务;3、传输层向高层屏蔽了下层数据通信的细节
网络层 进行逻辑地址寻址,实现不同网络之间的路径选择 处理网络间路由,确保数据及时传送(路由器),数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(传输路线)到达对方计算机,并把数据包传送给对方。
数据链路层 建立逻辑连接、进行硬件地址寻址、差错校验等功能 用来处理链接网络的硬件部分。负责无错传输数据,确认帧、发错重传等(交换机)
物理层 建立、维护、断开物理连接 定义物理设备如何传输数据;提供机械、电气、功能和过程特性(网卡、网线、双绞线、同轴电缆、中继器)

TCP/IP参考模型

网络五层结构

  • TCP/IP是传输控制协议/网络互联协议的简称
  • 早期的TCP/IP模型是一个四层结构,从下往上依次是网络接口层、互联网层、传输层和应用层
  • 后来在使用过程中,借鉴OSI七层参考模型,将网络接口层划分为了物理层和数据链路层,形成五层结构
分层 协议
应用层 HTTP(超文本传输协议)、FTP(文件传输协议)、TFTP、SMTP(发送邮件)、SNMP、DNS(域名系统)。。。
传输层 TCP(传输控制协议)、UDP(用户数据报协议)。。。
网络层 ICMP(网际控制消息协议,发送消息,并报告有关数据包的传送错误)、IGMP(互联组管理协议,IP主机向本地多路广播路由器报告主机组成员)、IP(网际协议,负责主机和网络之间寻址和路由数据包)、ARP(地址解析协议,获得同一物理网络中的硬件主机MAC地址)。。。
数据链路层 由底层网络定义的协议
物理层 由底层网络定义的协议

数据包封装

上层协议数据是如何转变为下层协议数据的呢?

这是通过封装(encapsulate)来实现的。应用程序数据在发送到物理网络之前,会沿着协议栈从上往下传递。每层协议都将在上层协议数据的基础上加上自己的头部信息(链路层还会加上尾部信息),以为实现该层功能提供必要的信息。 发送端发送数据时,数据会从上层传输到下层,且每经过一层都会被打上该层的头部信息。而接收端接收数据时,数据会从下层传输到上层,传输前会把下层的头部信息删除。

为什么要这样封装呢?

由于下层协议的头部信息对上层协议是没有实际的用途,所以在下层协议传输数据给上层协议的时候会把该层的头部信息去掉,这个封装过程对于上层协议来说是完全透明的。这样做的好处是,应用层只需要关心应用服务的实现,而不用管底层的实现。

与HTTP关系亲密的协议:IP、TCP、DNS

1、IP

按层此分,IP位于网络层。在TCP/IP族中IP指网际协议,不要与IP地址混淆。

IP地址:指明了节点被分配到的地址,

MAC地址:是指网卡所属的固定地址;

IP地址可以和MAC地址进行配对,IP地址可变换,但MAC地址基本上不会更改。IP间的通信依赖MAC地址。

不在同一个局域网时,我们会采用ARP协议(是一种用于解析地址的协议),根据通信方的IP地址就可以反查出对应的MAC地址。

2、TCP

客户端和服务端进行信息发送,是需要创建一个TCP连接的。按层此分,TCP(Transimision Control Protocal)位于传输层。提供可靠的字节流服务(为了方便传输将大块数据分割成以报文段为单位的数据包进行管理)。可靠的传输服务是指,能够把数据准确可靠的传给对方。

TCP 慢启动

TCP 连接会随着时间进行自我「调谐」,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐则被称为 TCP 慢启动。

2.1、TCP功能:

  • 连接建立 : 主要是指TCP/IP的三次握手来建立连接
  • 将数据进行分段打包传输: 主要是实在网络传输过程中的MTU(最大传输单元)决定的,数据必须打包成段才可以传送 ;
  • 对每个数据包编号控制顺序: 数据打包成段后,需要按序列号排序来发送,保证数据的连贯性
  • 运输中丢失、重发和丢弃处理
  • 流量控制: 通过滑动窗口来进行流量的控制
  • 避免拥塞: 解决拥塞控制的办法是:采用慢启动和拥塞避免算法结合使用来控制拥塞

2.2、TCP的状态

常见的TCP状态有:CLOSED, LISTEN, SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT1, CLOSE_WAIT, FIN_WAIT2, LAST_ACK, TIME_WAIT, CLOSED。tcp协议通过tcp状态来标记当前处于通信过程的哪个阶段。

2.3、TCP协议与UDP协议

  • TCP(Transimision Control Protocal)
    • 传输控制协议
    • 可靠的、面向连接的协议
    • 传输效率低
  • UDP(User Datagram Protocal)
    • 用户数据报协议
    • 不可靠的、无连接的服务
    • 传输效率高
  • TCP与UDP的区别:可以看出 TCP协议相对于UDP协议的特点是:TCP协议提供面向连接、字节流和可靠的传输。

2.4、UDP的应用

  • QQ
  • 视频软件
  • TFTP 简单文件传输协议(短信)

2.5、三次握手、数据传输、四次挥手

为了防止服务器开启无用的链接。

2.5.1、三次握手

为了准确无误地将数据传送到目标处,TCP才用了三次握手策略。

注意:

  • TCP是面向连接的协议,它在源点和终点之间建立虚拟连接,而不是物理连接
  • 在数据通信之前,发送端与接收端要先建立连接,等数据发送结束后,双方再断开连接
  • TCP连接的每一方都是由一个IP地址和一个端口组成
  • http是不存在连接这个概念的,只有请求和响应,他们都是数据包

具体过程如下:

**第一次握手:**客户端发送带有SYN标志的数据段链接请求报文段给服务端, 通过该数据段告诉服务端希望建立连接,需要服务端应答,并告诉服务端传输的起始序列号,然后进入SYN_SEND状态,等待服务端的确认。

**第二次握手:**服务端接收到客户端的SYN报文段后,需要发送ACK信息对这个SYN报文段进行确认。同时,还要发送自己的SYN请求信息。 一是发送ACK告诉客户端收到了数据段,二是通知客户端从哪个序列号做标记。服务端会将上述的信息放到一个报文段(SYN+ACK报文段)中,ack等于seq的值+1,一并发送给客户端,此时服务端将会进入SYN_RECV状态。

第三次握手:客户端接收到服务端的SYN+ACK报文段后,会想服务端发送ACK确认报文段,这个报文段发送完毕后,客户端和服务端都进入ESTABLISHED状态,完成TCP三次握手。

当三次握手完成后,TCP协议会为连接双方维持连接状态。为了保证数据传输成功,接收端在接收到数据包后必须发送ACK报文作为确认。如果在指定的时间内(这个时间称为重新发送超时时间),发送端没有接收到接收端的ACK报文,那么就会重发超时的数据。

2.5.2、数据传输

  • 客户端先向服务器发送数据,该数据报是长度为159的数据。
  • 服务器收到报文后, 也向客户端发送了一个数据进行确认(ACK),并且返回客户端要请求的数据,数据的长度为111,将seq设置为1,ack设置为160(1 + 159)。
  • 客户端收到服务器返回的数据后进行确认(ACK),将seq设置为160, ack设置为112(1 + 111)。

2.5.3、四次断开

  • 客户端发送FIN控制位发出断开连接的请求
  • 服务器进行响应,确认收到断开连接请求
  • 服务器提出反方向的关闭要求
  • 客户端确认收到的主机B的关闭连接请求

** 3、DNS**

DNS是Domain Name Service的缩写,位于应用层,DNS服务器进行域名和与之对应的IP地址转换的服务器

通常我们访问一个网站,使用的是主机名或者域名来进行访问的。因为相对于IP地址(一组纯数字),域名更容易让人记住。但TCP/IP协议使用的是IP地址进行访问的,所以必须有个机制或服务把域名转换成IP地址。DNS服务就是用来解决这个问题的,它提供域名到IP地址之间的解析服务。 即DNS协议提供通通过域名查找IP地址,或逆向从IP地址反查域名的服务。

DNS域名解析过程:

当用户在浏览器地址栏输入URL,回车后,我们要找URL相应的IP地址,怎么找呢?

如上图,浏览器先找自身缓存,让其查找是否有缓存的记录,结果并没有发现,此时找向系统缓存,主要去查找了系统中的hosts文件,同样没有,此时找向路由器缓存,查看路由器映射表,然而,并没有!于是,计算机将域名发给了本地DNS服务器(提供本地连接的服务商),本地DNS服务器找不到会将域名发送给其他服务器,进行递归过程,首先会发送到根域名服务器去找,返回顶级域名服务器的IP地址,再请求顶级域名服务器IP返回二级域名服务器IP,再请求二级域名服务器IP返回三级域名服务器IP......直到找到对应的IP地址,返回给浏览器。

DNS负载均衡:

DNS负载均衡,又叫做DNS重定向。 CDN(Content Delivery Network)就是利用DNS的重定向技术,DNS服务器会返回一个跟用户最接近的点的IP地址给用户,CDN节点的服务器负责响应用户的请求,提供所需的内容。

DNS返回的IP地址是否每次都一样?如果每次都一样是否说明你请求的资源都位于同一台机器上面,那么这台机器需要多高的性能和储存才能满足亿万请求呢?

其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡。

4、TCP/IP族与HTTP相关协议关系

socket 通信机制

Socket是IPC通信的一种方式,用于实现在同一主机或者不同主机之间的通信。socket通信在domain中实现,所谓的 domain 是识别一个socket的方法(socket地址格式)。

socket是一组实现TCP/UDP通信的接口API,既无论TCP还是UDP,通过对scoket的编程,都可以实现TCP/UCP。

1、常见的domain:

  • Unix Domain: 基于socket机制实现同一主机不同进程间通信的一种方式;AF_UNIX, AF_LOCAL,地址是一个路径名(文件)
  • IPv4 Domain: AF_INET, 基于socket机制借助于ipv4协议实现不同主机(也可以是同一主机)上的进程间通信的机制; 地址是32位的ipv4地址+16位的端口号
  • IPv6 Domain: AF_INET6, 地址是128位的Ipv6地址+16位的端口号

2、socket的类型:

  • TCP:流式socket,SOCK_STREAM 提供可靠、双向、面向字节流
  • UDP:数据报式socket, SOCK_DGRAM

3、相关的系统调用:

  • socket( ): 创建一个新的socket
  • bind( ):绑定于一个套按字地址和端口上
  • listen( ): 监听套接字
  • accept( ): 接收连接请求
  • connect( ): 发起连接请求
  • close( ): 关闭连接
  • read( ):从套接字向缓冲区读入数据
  • write( ): 从缓存区向套接字中写入数据

URL、URI

URI用字符串标识某一互联网资源,而URL表示资源的地点(互联网上所处的位置)。URL是URI的子集。

1、URI

URI(Uniform Resource Identifier)是统一资源标识符,在某个规则下能把这个资源独一无二标示出来,比如人的身份证号。

  • Uniform:规定统一的格式客房部处理多种不同类型的资源,而不用根据上下文来识别资源指定的访问方式;
  • Resource:可以标识的任何东西
  • Identifier:表示可标识的对象

2、URL

URL(Uniform Resource Locator)统一资源定位符,表示资源的地点,URL正是使用浏览器访问WEB页面时需要输入的网页地址

  • Uniform 不用根据上下文来识别资源指定的访问方式
  • Resource 可以标识的任何东西
  • Location 定位

2.1、URL的格式

  • 协议类型:使用 http, https, file等协议方案名获取访问资源时要指定协议类型。也可以使用data:或者javascript:这类指定数据或者脚本程序的方案名。不区分大小写,最后附一个冒号。后面必须和://连在一起。
  • 登录信息:可选项。指定用户名和密码作为从服务器端货物资源时必要的登录信息。 很不安全,不推荐使用,也不常用。
  • 服务器地址:服务器地址
  • 服务器端口号:服务器端口号
  • 带层次的文件路径: 表示请求路径,标记资源所在位置。
  • 查询字符串: 表示查询参数,为key=val这种形式,多个键值对之间用&隔开。
  • 片段标识符: 表示 URI 所定位的资源内的一个锚点,浏览器可以根据这个锚点跳转到对应的位置

HTTP协议

HTTP简介

  • HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写。
  • HTTP协议能够明确区分哪端是客户端,哪端是服务器端。请求的一方叫客户端,响应的一方叫服务器端。
  • 通过请求和响应的交换达成通信
  • HTTP协议是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
  • HTTP是一个应用层的面向对象协议, 是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
  • HTTP是一个无状态的协议。
  • 默认HTTP的端口号为80,HTTPS的端口号为443。
  • HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。如下图:

HTTP特点

1、无连接:限制每次链接只处理一个请求。 服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 早期这么做的原因是请求资源少,追求快。后来通过Connection: Keep-Alive实现长连接 。

2、无状态: HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这是为了更快的处理大量事务,确保协议的可伸缩性,而特意设计的。另一方面,在服务器不需要先前信息时它的应答就较快。 协议对于发送过的请求或相应都不做持久化处理。HTTP/1.1虽然无状态,但是增加了cookie技术。有了cookie再用HTTP协议通信,就可以管理状态了。之后会讲解。

3、简单快速: 客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

4、灵活: HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 主要体现在两个方面: 一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。

6、支持客户端/服务器模式

7、HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。

注意:!!!

1、HTTP是无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,HTTP使用的不是UDP协议(无连接) 2、从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接 3、Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间

HTTP版本

它的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的RFC,RFC 1945定义了HTTP/1.0版本。其中最著名的就是RFC 2616。RFC 2616定义了今天普遍使用的一个版本——HTTP /1.1。

HTTP/0.9

1990年问世。并没有作为正式的标准被建立。现在的HTTP其实含有HTTP/1.0之前版本的意思,因此被称为HTTP/0.9。只有一个命令GET

改版本特别简单,只有一个命令GET.

GET /index.html

上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页index.html

协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式,比如头部信息。

<html>
<body>Hello World</body>
</html>

服务器发送完毕,就关闭TCP连接。

HTTP/1.0

1996年5月,HTTP/1.0 版本发布,内容大大增加。 增加了很多命令、增加status、code和header、多字符集支持、多部分发送、权限、缓存等。 描述HTTP 1.0规范的RFC 1945。

HTTP/1.0 新特性:

1、增加方法: 除了GET方法,还引入了POST方法和HEAD方法;

2、 任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件;

3、 HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。

4、 其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。

HTTP/1.0缺点:

缺点:每个TCP链接只能发送一个请求。 发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。

解决方法:使用,Connection:keep-alive

HTTP/1.1:

1997年1月,HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了20年后的今天,直到现在还是最流行的版本。

在HTTP/1.0的基础上,支持了持久连接、增加了pipeline、增加host和其他一些命令。

描述HTTP 1.1规范的RFC 2616。

HTTP/1.1 新特性

1、 引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive

客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。

Connection: close

目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。

2、 HTTP管线化(HTTP pipelining)

引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。

3、与HTTP/1.0不同,还有Content-Length 字段;

因为一个 TCP连接可以发多个请求,那现在就会传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。所以这个字段用于声明本次回应的数据长度。

而HTTP/1.0版本中, 浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了,所以 Content-Length字段 不是必须的。

4、相对于HTTP/1.0,1.1版还新增了许多动词方法:PUTPATCHHEADOPTIONSDELETE

另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。

http/1.1缺点:

1、"队头堵塞"(Head-of-line blocking): 1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。

解决方法:

  • 将同一页面的资源分散到不同域名下,提升连接上限。 Chrome有个机制,对于同一个域名,默认允许同时建立 6 个 TCP持久连接,使用持久连接时,虽然能公用一个TCP管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。另外如果在同一个域名下同时有10个请求发生,那么其中4个请求会进入排队等待状态,直至进行中的请求完成。
  • Spriting合并多张小图为一张大图,再用JavaScript或者CSS将小图重新“切割”出来的技术。
  • 内联(Inlining)是另外一种防止发送很多小图请求的技巧,将图片的原始数据嵌入在CSS文件里面的URL里,减少网络请求次数。
  • 拼接(Concatenation)将多个体积较小的JavaScript使用webpack等工具打包成1个体积更大的JavaScript文件,但如果其中1个文件的改动就会导致大量数据被重新下载多个文件。

2、HTTP头部巨大

3、明文传输--带来的不安全性

4、服务器不能主动推送

**SPDY **

上面我们提到,由于HTTP/1.1的缺点,想解决的话,我们会合并脚本和样式表、雪碧图、将图片嵌入CSS代码、域名分片(domain sharding)等等的方式来提高性能。不过这些优化都绕开了协议,直到2009年,谷歌公开了自行研发的 SPDY 协议,主要解决HTTP/1.1效率不高的问题。谷歌推出SPDY,才算是正式改造HTTP协议本身。降低延迟,压缩header等等,SPDY的实践证明了这些优化的效果,也最终带来HTTP/2的诞生。

HTTP/2:

简介:

2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。 所有数据以二进制传输、同一个连接里面发送多个请求不再需要按照顺序来了、头信息压缩以及推送(服务端可以主动发起请求了)等提高了效率的功能

  • HTTP/2是现行HTTP协议(HTTP/1.x)的替代,但它不是重写;
  • HTTP方法/状态码/语义都与HTTP/1.x一样。
  • HTTP/2基于SPDY,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection)
  • 从目前的情况来看,国内外一些排名靠前的站点基本都实现了HTTP/2的部署,使用HTTP/2能带来20%~60%的效率提升。

HTTP/2由两个规范(Specification)组成:

  1. Hypertext Transfer Protocol version 2 - RFC7540
  2. HPACK - Header Compression for HTTP/2 - RFC7541

HTTP/2新特性:

1、二进制协议

HTTP/2基于SPDY的,是一个二进制协议,而HTTP/1.x是一个超文本协议; HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。

HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码

2、多路复用

HTTP/2可以避免 "队头堵塞" ,因为HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应; 双向的、实时的通信,就叫做多工;

大家可以通过 该链接 直观感受下 HTTP/2 比 HTTP/1 到底快了多少。

3、 HTTP/2 引入了头信息压缩机制(header compression)

4、 HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)

HTTP报文

用于HTTP协议交互的信息被称为HTTP报文。请求端(客户端)的HTTP报文叫做请求报文,响应端(服务端)的叫做响应报文。必定包含HTTP首部

HTTP报文构成:报文首部、空行、报文主体。通常,并不一定要有报文主体。

请求报文和响应报文的构成

从上边图可以看出,请求报文:请求行、各种首部字段、空行;响应报文:状态行、各种首部字段、报文主体;

一般有四种首部:通用首部、请求首部、响应首部和实体首部;

请求报文的构成:

请求行:包括用于请求的方法,请求URI和HTTP版本。如下图请求报文的构成:POST方法、请求URI:/form/entry 、协议版本:HTTP/1.1

请求首部字段:包含请求各种条件和属性的各类首部。

响应报文:

状态行:包含表明响应结果的状态码,原因短语和HTTP版本;

响应首部字段:表示响应的各种条件和属性的各类首部;

响应体:具体的数据,如下图返回的html

ps:注意事项

1、在起始行(请求行和状态行)中,每两个部分之间用空格隔开,最后一个部分后面应该接一个换行,严格遵循ABNF语法规范;

2、首部字段:

  • 字段名不区分大小写
  • 字段名不允许出现空格,不可以出现下划线_
  • 字段名后面必须紧接着:

3、空行

很重要,用来区分开头部实体

问: 如果说在头部中间故意加一个空行会怎么样?

那么空行后的内容全部被视为实体。

利用浏览器来查看网页的报文

我们使用 Chrome浏览器的开发者工具中的Network 来查看浏览器和服务器之间的通信;

step1:在浏览器地址栏中输入www.baidu.com,在ctrl+f12或在菜单中选择“视图”,“开发者”,“开发者工具”,就可以显示开发者工具 ;

step2: 我们点Network,确保第一个小红灯亮着(抓包工具),Chrome就会记录所有浏览器和服务器之间的通信:

step3: 在Network中,定位到第一条记录,点击,右侧将显示Request Headers,点击右侧的view source,我们就可以看到浏览器发给百度服务器的请求:

step4:同样你还可以看Response Headers,点击view source,显示服务器返回的原始响应数据

Content-Type指示响应的内容,这里是text/html表示HTML网页。 浏览器就是依靠Content-Type来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠URL来判断响应的内容, 所以,即使URL是http://example.com/abc.jpg,它也不一定就是图片。

step5:点击Response是响应体的内容是HTML源码。

当浏览器读取到百度首页的HTML源码后,它会解析HTML,显示页面,然后,根据HTML里面的各种链接,再发送HTTP请求给新浪服务器,拿到相应的图片、视频、Flash、JavaScript脚本、CSS等各种资源,最终显示出一个完整的页面。所以我们在Network下面能看到很多额外的HTTP请求。

利用curl工具查看报文

step1: 在命令行工具中输入下面命令

curl -v www.baidu.com

当然还可以使用其他的抓包工具查看,在这就不一一列举了。

HTTP请求方法:

注意方法名要大写。

方法 说明 支持版本
GET 获取资源。请求指定的页面信息,并返回实体主体。 HTTP/1.0以上版本都支持
POST 向服务器发送数据,传输实体主题。请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。 HTTP/1.0以上版本都支持
PUT 传输文件。HTTP/1.1的PUT不带验证机制,所以一般的web网站也不会使用此方法。 HTTP/1.0以上版本都支持
HEAD 只请求页面的首部。 HTTP/1.0以上版本都支持
DELETE 请求服务器删除指定的页面。与PUT相反的方法。HTTP/1.1的DELETE也不带验证机制,所以一般的web网站也不会使用此方法。 HTTP/1.0以上版本都支持
OPTIONS 询问支持的方法。此方法用来查询针对请求URI指定的资源支持的方法。(跨域时,有可能会用到,复杂请求也可会用到) HTTP/1.1以上版本都支持
TRACE 追踪路径。此方法让web服务器将之前的请求通信返回给客户端的方法。此方法不常用,容易引发XST(跨站追踪)攻击。 HTTP/1.1以上版本都支持
CONNECT 要求用隧道协议连接代理。主要使用SSL(安全套接层)和TLS(传输层安全)协议把通信内容加密后经网络隧道传输。 HTTP/1.1以上版本都支持
LINK 请求服务器建立链接关系。 HTTP/1.0版本支持,HTTP/1.1已经废除
UNLINK 断开链接关系。 HTTP/1.0版本支持,HTTP/1.1已经废除

GET和POST的区别

后又有这样一些具体的差别:

  • 缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
  • 从**编码**的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
  • 从**参数**的角度,GET 一般放在 URL的后面拼接 上,因此不安全,POST 放在请求体中,更适合传输敏感信息。
  • 从**安全**角度, POST的安全性要比GET的安全性高。
  • 长度限制的角度,GET请求有具体长度限制,一般不超过1024KB,而POST理论上没有,但是浏览器本身都有一个界限。
  • 从**幂等性的角度,GET幂等**的,而POST不是。(幂等表示执行相同的操作,结果也是相同的)
  • 从**TCP**的角度,GET和POST都是TCP连接,并无实质的区别.但是由于HTTP/浏览器的限定,导致它们在应用过程中体现出了一些不同.GET产生一个数据包,POST产生两个数据包.对于GET请求,浏览器会把http header 和 data 一并发出去,服务器响应200(返回数据).而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok (火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

HTTP状态码及含义

状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理请求,还是出现了错误。

响应码 含义、应用
1** 表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应。
100 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101 切换协议。请求者已要求服务器切换协议,服务器已确认并准备切换。针对请求头的Upgrade返回的信息。表明服务器正在切换到指定的协议。比如websocket、升级到http2
2** 表明服务器成功的接受了客户端请求
200 成功。客户端请求已成功。通常,这表示服务器提供了请求的网页。
201 已创建。请求成功并且服务器创建了新的资源。常用于POST,PUT 请求,表明请求已经成功,并新建了一个资源。并在响应体中返回路径。
202 已接受。请求已经接收到,但没有响应,稍后也不会返回一个异步请求结果。 该状态码适用于等待其他进程处理或者批处理的场景。
203 非权威性信息。服务器已成功处理了请求,但返回的信息可能来自另一来源。主要用于其他资源的镜像和备份。除了前面的情况,首选还是200。
204 无内容。服务器成功处理了请求,没有返回任何内容,但是头信息有用。用户代理(浏览器)会更新缓存的头信息。用户代理: 代替用户运行的软件,如web浏览器,或者邮件阅读器。
205 重置内容。告诉用户代理(浏览器)重置发送该请求的文档。
206 部分内容。表明已部分下载了一个文件。可以续传损坏的下载,或者将下载拆分为多个并发的流。服务器成功处理了部分 GET 请求。当客户端使用Range请求头时,返回该状态码。curl -v --header "Range:bytes=0-3",通过curl发起http请求-->响应行为:HTTP/1.1 206 Partial Content
207 多状态(WebDAV)。此消息之前应该还有一条 XML 消息,其中可能包含几个单独的响应代码,具体取决于发出了多少个子请求。
3** 重定向。例如,浏览器可能不得不请求服务器上的不同页面,或通过代理服务器重复该请求。
301 已永久移动。此请求和之后所有的请求都应该转到指定的 URI。因为有些客户端会把请求方式method改成GET。所以该状态码建议GET和HEAD方法中使用。搜索引擎会更新地址到资源的链接(SEO中‘link-judge’被发送到新的URL)。
302 对象已移动。未来可能还会有新的修改。对于基于表单的身份验证,此消息通常表示为“对象已移动”。请求的资源临时驻留在不同的 URI。由于重定向有时可能会改变,客户端将来在请求时应该继续使用 RequestURI。只有在 CacheControl 或 Expires 标题字段中指示,此响应才能够缓存。搜索引擎不会更改URL到资源的。应用:负载均衡。
304 未修改。客户端请求的文档已在其缓存中,文档自缓存以来尚未被修改过。客户端使用文档的缓存副本,而不从服务器下载文档。如果想使用200状态码达到相同304效果,需要强制缓存,需要额外的请求头:Cache-Control, Expires, Vary
305 使用代理。
307 临时重定向。基本和302相同。唯一的区别是这个状态码严格禁止浏览器到新URL请求资源时修改原来的请求方式和请求体。比如,原来使用POST,这次还是要使用POST。如果想要用PUT方法去修改一个服务器上没有的资源,可以用303状态码。如果想要把一个POST方法改为GET,请使用303。
308 永久重定向。基本和301相同。但是严格禁止修改请求方式和请求体。
4** 客户端错误,域名已停止加速服务。。例如,客户端请求不存在的页面,客户端未提供有效的身份验证信息。
400 请求语法有问题,服务器无法识别。例如,没有host请求头字段,或者设置了超过一个的host请求头字段。
401 访问请求验证失败。缺乏有效的身份认证凭证,一般可能是未登陆。登陆后一般都解决问题。
401.1 用户名或密码无效导致登录失败。
401.2 服务器配置导致登录失败。
401.3 由于 ACL 对资源的限制而未获得授权。表示存在 NTFS 权限问题。即使您对试图访问的文件具备相应的权限,也可能发生此错误。例如,如果 IUSR 帐户无权访问 C:WinntSystem32Inetsrv 目录,您会看到这个错误。
401.4 筛选器授权失败。
401.5 ISAPI/CGI 应用程序授权失败。
401.7 由 Web 服务器上的 URL 验证策略拒绝访问。这个错误代码为 IIS 6.0 所专用。
402 保留,将来使用
403 服务器拒绝响应。权限不足。
404 URL无效或者URL有效但是没有资源。
405 方法禁用。请求方式Method不允许。但是GET和HEAD属于强制方式,不能返回这个状态码。
406 不接受。资源类型不符合服务器要求。
407 需要代理授权。要求进行代理身份验证。
408 请求超时。服务器等候请求时发生超时。
409 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
410 已删除。如果请求的资源已永久删除,服务器就会返回此响应。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置。
411 需要有效长度。服务器不接受不含有效内容长度Content-Length标头字段的请求。
412 未满足前提条件。客户端请求信息的先决条件错误。
413 请求实体过大。由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息。
414 请求的 URI 过长。请求的 URI(通常为网址)过长,服务器无法处理。
415 不支持的媒体类型。服务器无法处理请求附带的媒体格式
416 客户端请求的范围无效。
417 服务器无法满足Expect的请求头信息。
5** 服务器错误。
500 服务器内部错误。未捕获。
501 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
502 错误网关。Web 服务器作为网关或代理服务器时,从上游服务器收到了无效响应。此类错误一般与服务器本身有关(与请求无关)、负载均衡。
503 服务不可用。目前服务器无法使用,一般是因为服务器超载或停止维护。通常,这只是暂时状态。一般还会伴随着返回一个响应头Retry-After: 说明恢复服务的估计时间。
504 网关超时。服务器作为网关或者代理,不能及时从上游服务器获取响应返回给客户端。
505 HTTP 版本不受支持。发出的请求http版本服务器不支持。如果请求通过http2发送,服务器不支持http/2,就会返回该状态码。

这么多大家可能记不住,不过下边常见的状态码,跟我们前端是息息相关的,需要记住。

常见状态码可见问题部分:HTTP响应码你都知道哪些?都是什么意思?

HTTP首部字段

HTTP首部字段有首部字段名和首部字段值构成,中间用冒号:分割

一般有四种首部:通用首部、请求首部、响应首部和实体首部;

首部字段:

  • 字段名不区分大小写

  • 字段名不允许出现空格,不可以出现下划线_

  • 字段名后面必须紧接着:

  • 字段值对应单个HTTP首部字段可以有多个值

    Keep-Alive: timeout=15,max=100
    

以下是HTTP\1.1规范定义的首部字段

1 、通用首部字段

首部字段名 说明
Cache-Control 控制缓存行为
Connection 链接的管理
Date 表明创建 HTTP 报文的日期和时间
Pragma 报文指令
Trailer 报文尾部的首部
Trasfer-Encoding 指定报文主体的传输编码方式
Upgrade 升级为其他协议,
首部字段 Upgrade 用于检测 HTTP 协议及其他协议是否可使用更高的版本进行通信,
其参数值可以用来指定 一个完全不同的通信协议。
Via 代理服务器信息
使用首部字段 Via 是为了追踪客户端与服务器之间的请求和响应报文的传输路径。
Warning 错误通知

2、 请求首部字段

首部字段名 说明
Accept 用户代理可处理的媒体类型
Accept-Charset 优先的字符集
Accept-Encoding 优先的编码
Accept-Langulage 优先的语言
Authorization Web认证信息
Expect 期待服务器的特定行为
From 用户的电子邮箱地址
Host 请求资源所在的服务器
If-Match 比较实体标记
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记
If-Range 资源未更新时发送实体Byte的范围请求
If-Unmodified-Since 比较资源的更新时间(和If-Modified-Since相反)
Max-Forwards 最大传输跳数
Proxy-Authorization 代理服务器需要客户端认证
Range 实体字节范围请求
Referer 请求中的URI的原始获取方
TE 传输编码的优先级
User-Agent HTTP客户端程序的信息

3、 响应首部字段

首部字段名 说明
Accept-Ranges 是否接受字节范围
Age 资源的创建时间
ETag 资源的匹配信息
Location 客户端重定向至指定的URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 再次发送请求的时机
Server 服务器的信息
Vary 代理服务器缓存的管理信息
www-Authenticate 服务器对客户端的认证

4、 实体首部字段

首部字段名 说明
Allow 资源可支持的HTTP方法
Content-Encoding 实体的编码方式
Content-Language 实体的自然语言
Content-Length 实体的内容大小(字节为单位)
Content-Location 替代对应资源的URI
Content-MD5 实体的报文摘要
Content-Range 实体的位置范围
Content-Type 实体主体的媒体类型
Expires 实体过期时间
Last-Modified 资源的最后修改时间

5、非HTTP/1.1首部字段

还有使用频率比较高的首部字段有:CookieSetCookieContene-Disposition

6、End-to-end 首部和 Hop-by-hop 首部

HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分成 2 种类型。

端到端首部(End-to-end): 分在此类别中的首部会转发给请求 / 响应对应的最终接收目标,且必须保存在由缓存生成的响应中,另外规 定它必须被转发。

逐跳首部(Hop-by-hop):分在此类别中的首部只对单次转发有效,会因通过缓存或代理而不再转发。HTTP/1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提供 Connection 首部字段。

下面列举了 HTTP/1.1 中的逐跳首部字段。除这 8 个首部字段之外,其他所有字段都属于端到端首部。

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • Trailer
  • TE
  • Transfer-Encoding
  • Upgrade

常见的报文头的属性

字段 说明 示例
Accept 可接收的响应内容类型 Accept:text/plain (文本类型)
Accept-Charset 可接收的字符集 Accept-Charset: utf-8
Accept-Encoding 可接受的响应内容的编码方式 Accept-Encoding: gzip, deflate
Accept-Language 可接受的响应内容语言列表 Accept-Language: en-US
Accept-Datetime 可接受的按照时间来表示的响应内容版本 Accept-Datetime: Sat, 26 Dec 2015 17:30:00 GMT
Authorization HTTP协议中需要认证资源的认证信息 Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE==
Cache-Control 请求/回复中的,是否使用缓存机制 Cache-Control: no-cache
Connection 客户端想要优先使用的连接类型 Connection: keep-alive Connection: Upgrade
Content-Length 以8进制表示的请求体的长度 Content-Length: 348
Content-Type 请求体的MIME类型 Content-Type: application/x-www-form-urlencoded
Date 发送该消息的日期和时间 Date: Dec, 26 Dec 2015 17:30:00 GMT
Expect 表示客户端要求服务器做出特定的行为 Expect: 100-continue
From 发起此请求的用户的邮件地址 From: user@a.com
Host 服务器域名和端口号,默认端口可省略 Host: www.a.com:80 or www.a.com
If-Match 主要用于PUT,实体匹配才可以操作 If-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"
If-Modified-Since 资源未被修改的情况下返回304未修改 If-Modified-Since: Dec, 26 Dec 2015 17:30:00 GMT
User-Agent 浏览器的身份标识字符串 User-Agent: Mozilla/
Upgrade 要求服务器升级到一个高版本协议 Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
Via 告诉服务器,这个请求是由哪个代理发出的 Via: 1.0 fred, 1.1 a.com.com (Apache/1.1)
Referer 表示跳转到当前页面的之前的页面 Referer: a.com/nodejs
Origin 发起一个针对跨域资源共享的请求 Origin: www.a.com

Connection

Connection 首部字段具备如下两个作用。

1、控制不再转发给代理的首部字段

2、管理持久连接

Connection: close

HTTP/1.1 版本的默认连接都是持久连接。为此,客户端会在持久连接上连续发送请求。当服务器端想明确断开连接时,则指定Connection 首部字段的值为 close

Pragma

Pragma 是 HTTP/1.1 之前版本的历史遗留字段,仅作为与 HTTP/1.0 的向后兼容而定义。 规范定义的形式唯一,如下所示。

Pragma: no-cache

该首部字段属于通用首部字段,但只用在客户端发送的请求中。客户端会要求所有的中间服务器不返回缓存 的资源。

所有的中间服务器如果都能以 HTTP/1.1 为基准,那直接采用Cache-Control: no-cache指定缓存的处理方式 是最为理想的。但要整体掌握全部中间服务器使用的 HTTP 协议版本却是不现实的。因此,发送的请求会同 时含有下面两个首部字段。

Cache-Control: no-cache
Pragma: no-cache

Cache-Control

通过指定首部字段 Cache-Control的指令,就能操作缓存的工作机制。

语法格式:

指令的参数是可选的,多个指令之间通过','分隔。

Cache-Control:private,max-age=0,no-cache

缓存请求指令

指令 参数 说明
no-cache 强制向源服务器再次验证
no-store 不缓存请求或响应的任何内容
max-age = [ 秒] 必需 响应的最大Age值
max-stale( = [ 秒]) 可省略 接收已过期的响应
min-fresh = [ 秒] 必须 期望在指定时间内的响应仍有效
no-transform 代理不可更改媒体类型
cache-extension - 新指令标记(token)

缓存响应指令

指令 参数 说明
public 可向任意方提供响应的缓存
private 可省略 仅向特定用户返回响应
no-cache 可省略 缓存前必须先确认其有效性
no-store 不缓存请求或响应的任何内容
no-transform 代理不可更改媒体类型
must-revalidate 可缓存但必须再向源服务器进行确认
proxy-revalidate 要求中间缓存服务器对缓存的响应有效性再进行 确认
max-age = [ 秒] 必须 响应的最大Age值
s-maxage = [ 秒] 必须 公共缓存服务器响应的最大Age值,max-age长得比较像,但是区别在于s-maxage是针对代理服务器的缓存时间
cache-extension - 新指令标记(token)

Content-Type

Content-Type(内容类型), 一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件。

Content-Type 标头告诉客户端实际返回的内容的内容类型。

关于字符的编码,1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。

语法格式:

Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something

常见的媒体格式类型如下:

媒体格式类型 说明
text/html HTML格式
text/plain 纯文本格式
text/xml XML格式
image/gif gif图片格式
image/jpeg jpg图片格式
image/png png图片格式

以application开头的媒体格式类型:

以application开头的媒体格式类型 说明
application/xhtml+xml XHTML格式
application/xml XML数据格式
application/atom+xml Atom XML聚合格式
application/json JSON数据格式
application/pdf pdf格式
application/msword Word文档格式
application/x-www-form-urlencoded 最常见的post提交数据的方式。 中默认的encType,浏览器原生的form表单,如果不设置enctype属性,那么最终就会以application/x-www-form-urlencoded 方式提交数据 。form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
application/octet-stream 二进制流数据(如常见的文件下载)

另外一种常见的媒体格式是上传文件之时使用的

媒体格式类型 说明
multipart/form-data 需要在表单中进行文件上传时,就需要使用该格式

Cookie

管理服务器与客户端之间状态的 Cookie,虽然没有被编入标准化 HTTP/1.1 的 RFC2616 中,但在 Web 网 站方面得到了广泛的应用。 Cookie 的工作机制是用户识别及状态管理。Web 网站为了管理用户的状态会通过 Web 浏览器,把一些数据 临时写入用户的计算机内。接着当用户访问该Web网站时,可通过通信方式取回之前发放的 Cookie。

为 Cookie 服务的首部字段

首部字段名 说明 首部类型
Set-Cookie 开始状态管理所使用的Cookie信息 响应首部字段
Cookie 服务器接收到的Cookie信息 请求首部字段

Cookie的处理流程:

1、 客户端第一次访问服务器的时候服务器通过响应头向客户端发送Cookie,属性之间用分号空格分隔

2、 客户端接收到Cookie之后保存在本地

3、 以后客户端再请求服务器的时候会把此Cookie发送到服务器端

Set-Cookie

语法格式:

Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 GMT; path=/; domain=.a.com;

当服务器准备开始管理客户端的状态时,会事先告知各种信息。下面的表格列举了 Set-Cookie 的字段值。

Set-Cookie 字段的属性

属性 说明
NAME=VALUE 赋予 Cookie 的名称和其值(必需项)
expires=DATE Cookie的有效期(若不明确指定则默认为浏览器关闭前为止)
max-age = [ 秒] Cookie多少秒后过期(若不明确指定则默认为浏览器关闭前为止)
path=PATH 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档 所在的文件目录)
domain=域名 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie 的服务 器的域名)
Secure 仅在 HTTPS 安全通信时才会发送 Cookie
HttpOnly 加以限制,使 Cookie 不能被 JavaScript 脚本访问,防止XSS攻击产生

expires 属性

Cookie 的 expires 属性指定浏览器可发送 Cookie 的有效期。 当省略 expires 属性时,其有效期仅限于维持浏览器会话(Session)时间段内。这通常限于浏览器应用程序被关闭之前。 另外,一旦 Cookie 从服务器端发送至客户端,服务器端就不存在可以显式删除 Cookie 的方法。但可通过覆盖已过期的 Cookie,实现对客户端 Cookie 的实质性删除操作。

Cookie

Cookie: status=enable

首部字段 Cookie 会告知服务器,当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。接收到多个 Cookie 时,同样可以以多个 Cookie 形式发送。

Cookie使用注意事项:

  • 可能被客户端篡改,使用前验证合法性
  • 不要存储敏感数据,比如用户密码,账户余额
  • 使用httpOnly保证安全
  • 尽量减少cookie的体积
  • 设置正确的domain和path,减少数据传输

session

session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而session保存在服务器上

客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了

cookie与session区别

  1. cookie数据存放在客户的浏览器上,session数据放在服务器上。
  2. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用session
  3. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用COOKIE
  4. 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie

将登陆信息等重要信息存放为session、其他信息如果需要保留,可以放在cookie中

缓存

通过⽹络获取内容既速度缓慢⼜开销巨⼤。较⼤的响应需要在客户端与服务器之间进⾏多次往返通信,

这会延迟浏览器获得和处理内容的时间,还会增加访问者的流量费⽤。因此,缓存并重复利⽤之前获取

的资源的能⼒成为性能优化的⼀个关键⽅⾯。

缓存作用

  • 减少了冗余的数据传输,节省了网费。
  • 减少了服务器的负担, 大大提高了网站的性能
  • 加快了客户端加载网页的速度

缓存分类

⼴义的缓存,可以分为这四个

    1. Http Cache
    1. Service Worker Cache:

      Service Worker 借鉴了 Web Worker的 思路,即让 JS 运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问DOM。虽然如此,但它仍然能帮助我们完成很多有用的功能,比如离线缓存消息推送网络代理等功能。其中的离线缓存就是 Service Worker Cache

    1. Memory Cache:

      内存缓存,从效率上讲它是最快的。但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。

    1. Push Cache:(HTTP/2中的服务器推送)

HTTP缓存

HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,可将其分为强制缓存,对比缓存。

  • 强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互
  • 两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则

1、强缓存

浏览器中的缓存作用分为两种情况,一种是需要发送HTTP请求,一种是不需要发送。

首先是检查强缓存,这个阶段不需要发送HTTP请求。

那么浏览器是如何检查的呢?

我们知道,在没有缓存数据的时候,浏览器向服务器请求数据时,服务器会将数据和缓存规则一并返回,缓存规则信息包含在响应header中。

注意:

HTTP/1.0HTTP/1.1当中,这个字段是不一样的。在早期,也就是HTTP/1.0时期,使用的是Expires,而HTTP/1.1使用的是Cache-Control。当ExpiresCache-Control同时存在的时候,Cache-Control会优先考虑。

Expires

Expires:即过期时间,存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。

比如下面这样:

Expires: Wed, 22 Apr 2020 08:41:00 GMT

表示资源在2020年4月22号8点41分过期,过期了就得向服务端发请求。

这个方式看上去没什么问题,合情合理,但其实潜藏了一个坑,那就是服务器的时间和浏览器的时间可能并不一致,那服务器返回的这个过期时间可能就是不准确的。因此这种方式很快在后来的HTTP/1.1版本中被抛弃了。

Cache-Control

Cache-Control: 在HTTP/1.1中,请求/响应头,缓存控制字段,精确控制缓存策略。

它和Expires本质的不同在于它并没有采用具体的过期时间点这个方式,而是采用过期时长来控制缓存,对应的字段是max-age。比如这个例子:

Cache-Control:max-age=3600

代表这个响应返回后在 3600 秒,也就是一个小时之内可以直接使用缓存。它其实可以组合非常多的指令,完成更多场景的缓存判断, 将一些关键的属性列举如下:

public: 客户端和代理服务器都可以缓存。因为一个请求可能要经过不同的代理服务器最后才到达目标服务器,那么结果就是不仅仅浏览器可以缓存数据,中间的任何代理节点都可以进行缓存。

private: 这种情况就是只有浏览器能缓存了,中间的代理服务器不能缓存。

no-cache: 跳过当前的强缓存,发送HTTP请求,即直接进入协商缓存阶段

no-store:非常粗暴,不进行任何形式的缓存。

s-maxage:这和max-age长得比较像,但是区别在于s-maxage是针对代理服务器的缓存时间。

must-revalidate: 是缓存就会有过期的时候,加上这个字段一旦缓存过期,就必须回到源服务器验证。

代码测试:

server.js

const http = require('http')
const fs = require('fs')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  if (request.url === '/') {
    const html = fs.readFileSync('test.html', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html'
    })
    response.end(html)
  }

  if (request.url === '/script.js') {
    response.writeHead(200, {
      'Content-Type': 'text/javascript',
      'Cache-Control': 'max-age=20'
    })
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')

cache.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>测试缓存</title>
</head>
<body>
    
</body>
<script src="/script.js"></script>
</html>

执行server.js可以用命令node server.js,在浏览器中的打开http://localhost:8888

当超过我们设置的时间max-age=20超过20s后,页面又会像我们第一次打开时一样。

2、协商缓存

当资源缓存时间超时了,也就是强缓存失效了 ,就进入协商缓存了。

  • 对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。
  • 浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。
  • 再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。

强缓存失效之后,浏览器在请求头中携带相应的缓存tag来向服务器发请求,由服务器根据这个tag,来决定是否使用缓存。 这样的缓存tag分为两种: Last-ModifiedETag

Last-Modified

Last-Modified:响应头,即最后修改时间。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。

浏览器接收到后,如果再次请求,会在请求头中携带If-Modified-Since字段,这个字段的值也就是服务器传来的最后修改时间。

If-Modified-Since: 请求头,资源最近修改时间,由浏览器告诉服务器。

服务器拿到请求头中的If-Modified-Since的字段后,其实会和这个服务器中该资源的最后修改时间对比:

  • 如果请求头中的这个值小于最后修改时间,说明是时候更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
  • 否则返回304,告诉浏览器直接用缓存。

ETag

ETag:响应头,资源标识,由服务器告诉浏览器。

ETag 是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过响应头把这个值给浏览器。

浏览器接收到ETag的值,会在下次请求时,将这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器。

If-None-Match: 请求头,缓存资源标识,由浏览器告诉服务器。

服务器接收到If-None-Match后,会跟服务器上该资源的ETag进行比对:

  • 如果两者不一样,说明要更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
  • 否则返回304,告诉浏览器直接用缓存。

两者对比

  1. 精准度上,ETag优于Last-Modified。优于 ETag 是按照内容给资源上标识,因此能准确感知资源的变化。而 Last-Modified 就不一样了,它在一些特殊的情况并不能准确感知资源变化,主要有两种情况:
    • 编辑了资源文件,但是文件内容并没有更改,这样也会造成缓存失效。
    • Last-Modified 能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的 Last-Modified 并没有体现出修改了。
  2. 在性能上,Last-Modified优于ETag,也很简单理解,Last-Modified仅仅只是记录一个时间点,而 Etag需要根据文件的具体内容生成哈希值。

另外,如果两种方式都支持的话,服务器会优先考虑ETag

Last-Modified存在问题

  1. 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
  2. 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒
  3. 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
  4. 如果同样的一个文件位于多个CDN服务器上的时候内容虽然一样,修改时间不一样。

代码测试

我们添加协商缓存:

server.js

const http = require('http')
const fs = require('fs')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  if (request.url === '/') {
    const html = fs.readFileSync('test.html', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html'
    })
    response.end(html)
  }

  if (request.url === '/script.js') {
    response.writeHead(200, {
      'Content-Type': 'text/javascript',
      'Cache-Control': 'max-age=20000000,no-cache',
      'Last-Modified':'123',
      'Etag':'666'
    })
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')

重新启动服务node server.js,打开浏览器http://localhost:8888/;

我们发现,虽然设置了max-age='20000000',但是刷新页面的时候,还是发送了请求,没有走缓存,因为还设置了no-cache,会进入协商缓存,我们设置了EtagLast-Modified,在第二次刷新页面的时候,请求头增加了If-None-MatchIf-Modified-Since且值跟EtagLast-Modified相对应;

缓存导致的静态资源等更改页面不改变问题

http/1.x中浏览器无法主动得知服务器上的静态资源变化没有,且 ExpiresCache-Control,他们都只能够控制缓存是否过期,但是在缓存过期之前,浏览器也是无法得知服务器上的资源是否变化的。只有当缓存过期后,浏览器才会发请求询问服务器。 那么这个时间(缓存没有过期的时间)我们上线了更改的静态资源,浏览器还是访问缓存的旧的资源,怎么解决呢?

解决方案:就是每次上线时,给静态资源文件名命名不一样;

一般我的处理方式是:

step1:我们不让 html 文件缓存,每次访问 html 都去请求服务器。所以浏览器每次都能拿到最新的html资源。 step2:将原来的文件名加上每次打包的版本号,或者时间戳、指纹(不要产生新文件, < script src="/script.js?_h=1.6wee1" > )、加哈希(),这样的好处是,可以知道静态资源时哪次打包的,哪些资源时这次更改的,如果出现错误,需要回溯版本,也可以快速回溯。

简单案例部分代码:

第一次时:我们在html使用的script.js

<script src="http://www.localhost:8888/script.js?version=1.0.1"></script>

ps: 浏览器下载1.0.1版本的script.js文件。 浏览器再次访问 html,发现还是1.0.1版本的script.js文件,则使用本地缓存。

某天我们需要更改script.js, 我们的html文件也相应变化如下:

<script src="http://www.localhost:8888/script.js?version=1.0.2"></script>

ps: 通过设置html不缓存,html引用资源内容变化则改变资源路径的方式,就解决了无法及时得知资源更新的问题。

其实这还是需要优化,为什么呢? 比如:页面引用了3个css,a.css、b.css、c.css;而某次上线只改了其中的a.css,如果所有链接都更新版本,就会导致b.css,c.css的缓存也失效,那岂不是又有浪费了?!那怎么办呢? 有人想到了将文件名和url联系起来,使用数据摘要要算法 对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种可以精确到单个文件粒度的缓存控制依据了。但是这在大公司的项目中也是不可以的,不是最优的。 为了进一步提升网站性能,会把静态资源和动态网页分集群部署,静态资源会被部署到CDN节点上,比如七牛或者自己公司的CND上,这时候是不是大家都有一个问号了?对静态资源和html放在不同的服务器了,那该先上线哪个呢???好难!!! 访问量不大的项目,可以让研发同学苦逼一把,等到半夜偷偷上线,先上静态资源,再部署页面,看起来问题少一些。

大公司的静态资源优化方案,基本上要实现这么几个东西:

  • 配置超长时间的本地缓存 —— 节省带宽,提高性能
  • 采用内容摘要作为缓存更新依据 —— 精确的缓存控制
  • 静态资源CDN部署 —— 优化网络请求
  • 更资源发布路径实现非覆盖式发布 —— 平滑升级

另外,使用webpack打包的话,借助插件可以很方便的处理,使用哈希。

缓存判断顺序

  • 1、先判断Cache-Control,在Cache-Control的max-age之内,直接返回200 from cache;
  • 2、没有Cache-Control再判断Expires,再Expires之内,直接返回200 from cache;
  • 3、Cache-Control=no-cache或者不符合Expires,浏览器向服务器发送请求;
  • 4、服务器同时判断ETag和Last-Modified,都一致,返回304,有任何一个不一致,返回200。

HTTP数据协商(内容协商)

在HTTP协议中,内容协商是这样一种机制,通过为同一URL指向的资源提供不同的展现形式,可以使用户代理选择与用户需求相适应的最佳匹配(例如: 文档使用的自然语言,图片的格式,文件格式,json、表单、或则内容编码形式)

当一项资源被访问的时候,特定展现形式的选取是通过内容协商机制来决定的,并且客户端和服务端之间存在多种协商方式。

请求声明Accept:

Accept: 声明我想要怎么样的数据,声明数据类型
Accept-Encoding: 代表数据是怎么样的编码方式,可以使用压缩
Accept-Language: 判断返回的信息是什么语言
User-Agent: 表示浏览器的一些相关的信息

与之对应的就是服务端Content:

Content-Type: 对应Accept,Accept可以接收很多种数据格式,Content-Type会在里面选择一种数据格式返回,在返回的时候声明返回的数据格式
Content-Encoding: 对应Accept-Encoding,告诉客户端,我究竟用了什么样的压缩数据的方式
Content-Language: 是否根据请求返回对应的语言

代码测试:

server.js

const http = require('http')
const fs = require('fs')
const zlib = require('zlib') // 引入包

http.createServer(function (request, response) {
  console.log('request come', request.url)

  const html = fs.readFileSync('test.html') // 这里不加 utf8,加了返回的就是字符串格式了
  response.writeHead(200, {
    'Content-Type': 'text/html',
    // 'X-Content-Options': 'nosniff'
    'Content-Encoding': 'gzip'
  })
  response.end(zlib.gzipSync(html)) // 压缩
}).listen(8888)

console.log('server listening on 8888')

test.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <form action="/form" id="form" enctype="application/x-www-form-urlencoded">
    <input type="text" name="name">
    <input type="password" name="password">
    <input type="submit">
  </form>
</body>
</html>

执行server.js可以用命令node server.js,在浏览器中的打开http://localhost:8888

将test.html中form提交方式改为POST

  <form action="/form" method="POST" id="form" enctype="application/x-www-form-urlencoded">

服务端根据 content-type 是 application/x-www-form-urlencoded来对body 中的数据进行转化即可

在刷新浏览器,填写表单内容,提交

增加文件的传输, 通过表单上传文件时,必须要把文件部分单独拆分出来,文件不能作为字符串进行传输的,要作为二进制的数据进行传输; multipart/form-data

test.html更改:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <form action="/form" method="POST" id="form" enctype="multipart/form-data">
    <input type="text" name="name">
    <input type="password" name="password">
    <input type="file" name="file">
    <input type="submit">
  </form>
  <script>
    var form = document.getElementById('form')
    form.addEventListener('submit', function (e) {
      e.preventDefault()
      var formData = new FormData(form)
      fetch('/form', {
        method: 'POST',
        body: formData
      })
    })
  </script>
</body>
</html>

提交文件格式我们表单的格式为:enctype="multipart/form-data"

浏览器中抓包的请求头中Content-Type

Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydHvHzymplSP4CAMk

请求体为:

------WebKitFormBoundarydHvHzymplSP4CAMk
Content-Disposition: form-data; name="name"

111
------WebKitFormBoundarydHvHzymplSP4CAMk
Content-Disposition: form-data; name="password"

222
------WebKitFormBoundarydHvHzymplSP4CAMk
Content-Disposition: form-data; name="file"; filename="16ba0ae535359e94.jpg"
Content-Type: image/jpeg


------WebKitFormBoundarydHvHzymplSP4CAMk--

boundary=----WebKitFormBoundarybwAbNlPF2bBcTLuA用来分割表单提交数据的各个部分

服务端拿到表单数据后,根据这个分割字符串,进行数据分割。

压缩

可以使用zlib模块进行压缩及解压缩处理,压缩文件以后可以减少体积,加快传输速度和节约带宽

accept-encoding:gzip //开启gzip

HTTP 压缩就是以缩⼩体积为⽬的,对 HTTP 内容进⾏重新编码的过程 Gzip 压缩背后的原理,是在⼀个⽂本⽂件中找出⼀些重复出现的字符串、临时替换它们,从⽽使整个⽂ 件变⼩。根据这个原理,⽂件中代码的重复率越⾼,那么压缩的效率就越⾼,使⽤ Gzip 的收益也就越 ⼤。反之亦然。

基本上来说,Gzip都是服务器⼲的活,⽐如nginx

压缩对象

压缩和解压缩对象都是一个可读可写流

  • zlib.createGzip:返回Gzip流对象,使用Gzip算法对数据进行压缩处理
  • zlib.createGunzip:返回Gzip流对象,使用Gzip算法对压缩的数据进行解压缩处理
  • zlib.createDeflate:返回Deflate流对象,使用Deflate算法对数据进行压缩处理
  • zlib.createInflate:返回Deflate流对象,使用Deflate算法对数据进行解压缩处理

代码案例:

var zlib = require('zlib');
var fs = require('fs');
var http = require('http');

var request = http.get({
    host: 'localhost',
    path: '/index.html',
    port: 9090,
    headers: {
        'accept-encoding': 'gzip,deflate'
    }
})

request.on('response', function (response) {
    var output = fs.createWriteStream('test.txt');
    switch (response.headers['content-encoding']) {
        case 'gzip':
            response.pipe(zlib.createGunzip()).pipe(output);
            break;
        case 'deflate':
            response.pipe(zlib.createInflate()).pipe(output);
            break;
        default:
            response.pipe(output);
            break;
    }
});
request.end();

href与src的区别

href (Hypertext Reference)指定网络资源的位置,从而在当前元素或者当前文档和由当前属性定义的需要的锚点或资源之间定义一个链接或者关系。(目的不是为了引用资源,而是为了建立联系,让当前标签能够链接到目标地址。)

src source(缩写),指向外部资源的位置,指向的内容将会应用到文档中当前标签所在位置。

href与src的区别

1、请求资源类型不同:href 指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的联系。在请求 src 资源时会将其指向的资源下载并应用到文档中,比如 JavaScript 脚本,img 图片;

2、作用结果不同:href 用于在当前文档和引用资源之间确立联系;src 用于替换当前内容;

3、浏览器解析方式不同:当浏览器解析到src ,会**暂停其他资源的下载和处理,**直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。

HTTP Redirect

重定向, 在response Header中增加Location,responseCode可以是3xx

原理:

在 HTTP 协议中,重定向操作由服务器通过发送特殊的响应(即 redirects)而触发。HTTP 协议的重定向响应的状态码为 3xx 。浏览器在接收到重定向响应的时候,会采用该响应提供的新的 URL ,并立即进行加载;大多数情况下,除了会有一小部分性能损失之外,重定向操作对于用户来说是不可见的。

不同类型的重定向映射可以划分为三个类别:

  • 永久重定向: 301、308, 它表示原 URL 不应再被使用,而应该优先选用新的 URL。搜索引擎机器人会在遇到该状态码时触发更新操作,在其索引库中修改与该资源相关的 URL 。 多用于网站重构。
  • 临时重定向:302、303、307, 有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问。在这种情况下可以使用临时重定向。搜索引擎不会记录该新的、临时的链接。在创建、更新或者删除资源的时候,临时重定向也可以用于显示临时性的进度页面。
  • 特殊重定向:300、304, 304(Not Modified,资源未被修改)会使页面跳转到本地陈旧的缓存版本当中(该缓存已过期(?)),而 300(Multiple Choice,多项选择) 则是一种手工重定向:以 Web 页面形式呈现在浏览器中的消息主体包含了一个可能的重定向链接的列表,用户可以从中进行选择。

优先级:

  1. HTTP 协议的重定向机制永远最先触发,即便是在没有传送任何页面——也就没有页面被(客户端)读取——的情况下。
  2. HTML 的重定向机制 (``) 会在 HTTP 协议重定向机制未设置的情况下触发。
  3. JavaScript 的重定向机制总是作为最后诉诸的手段,并且只有在客户端开启了 JavaScript 的情况下才起作用。

任何情况下,只要有可能,就应该采用 HTTP 协议的重定向机制,而不要使用 `` 标签。假如开发人员修改了 HTTP 重定向映射而忘记修改 HTML 页面的重定向映射,那么二者就会不一致,最终结果或者出现无限循环,或者导致其他噩梦的发生。

设定方法:

1、HTML重定向机制: 这种机制会使浏览器的回退按钮失效:可以返回含有这个头部的页面,但是又会立即跳转。

<head> 
  <meta http-equiv="refresh" content="0;URL=http://www.a.com/" />
</head>

2、JavaScript设置重定向

window.location = "http://www.a.com/";

3、在服务器中响应

例如我们在客服端发出一个请求

const http = require('http')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  if (request.url === '/') {
    response.writeHead(302, {  
      'Location': '/new' 
    })
    response.end()
  }
  if (request.url === '/new') {
    response.writeHead(200, {
      'Content-Type': 'text/html',
    })
    response.end('<div>this is content</div>')
  }
}).listen(8888)

console.log('server listening on 8888')

ps:使用 永久性重定向 要慎重,一旦使用,服务端更改路由设置,用户如果不清理浏览器缓存,就会一直重定向。

HTTP工作流程

一般的通信流程:首先客户端发送一个请求(request)给服务器,服务器在接收到这个请求后将生成一个响应(response)返回给客户端。

一次HTTP操作称为一个事务,其工作过程可分为四步:

1)首先客户端与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。

2)建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户端信息和可能的内容。

3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户端与服务器断开连接。

如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

HTTPS

HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL/TLS层。其所用的端口号是443。

HTTPS = HTTP+TLS/SSL

https通信的优点:

  • 1)客户端产生的密钥只有客户端和服务器端能得到;
  • 2)加密的数据只有客户端和服务器端才能得到明文;
  • 3)客户端到服务端的通信是安全的。

HTTPS和HTTP的区别主要如下:

  1. HTTPS协议需要到CA(证书颁发机构)申请证书,一般免费证书很少,需要交费。
  2. HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
  3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由HTTP+SSL协议构建的可进行加密传输、身份认证的网络协议,可以有效的防止运营商劫持,解决了防劫持的一个大问题,比http协议安全。

HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法:

  • 散列函数 散列函数验证信息的完整性

  • 对称加密:对称加密算法采用协商的密钥对数据加密,密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES,RC5,3DES等;

    对称加密主要问题是共享秘钥,除你的计算机(客户端)知道另外一台计算机(服务器)的私钥秘钥,否则无法对通信流进行加密解密。解决这个问题的方案非对称秘钥。

  • 非对称加密:非对称加密实现身份认证和密钥协商,使用两个秘钥:公共秘钥和私有秘钥。私有秘钥由一方密码保存(一般是服务器保存),另一方任何人都可以获得公共秘钥。

    这种密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。

代码案例:

1、对称加密:

const crypto = require('crypto');
function encrypt(data, key, iv) {
    let decipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    decipher.update(data);
    return decipher.final('hex');
}

function decrypt(data, key, iv) {
    let decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    decipher.update(data, 'hex');
    return decipher.final('utf8');
}

let key = '1234567890123456';
let iv = '1234567890123456';
let data = "hello";
let encrypted = encrypt(data, key, iv);
console.log("数据加密后:", encrypted);
let decrypted = decrypt(encrypted, key, iv);
console.log("数据解密后:", decrypted);

2、非对称加密:

let { generateKeyPairSync, privateEncrypt, publicDecrypt } = require('crypto');
let rsa = generateKeyPairSync('rsa', {
    modulusLength: 1024,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase: 'server_passphrase'
    }
});
let message = 'hello';
let enc_by_prv = privateEncrypt({
    key: rsa.privateKey, passphrase: 'server_passphrase'
}, Buffer.from(message, 'utf8'));
console.log('encrypted by private key: ' + enc_by_prv.toString('hex'));


let dec_by_pub = publicDecrypt(rsa.publicKey, enc_by_prv);
console.log('decrypted by public key: ' + dec_by_pub.toString('utf8'));

使用md5加密:

var crypto = require('crypto');
var content = '123456';
var result = crypto.createHash('md5').update(content).digest("hex")
console.log(result);//32位十六进制 = 128位二进制

sha256加密:

const salt = '123456';
const sha256 = str => crypto.createHmac('sha256', salt)
    .update(str, 'utf8')
    .digest('hex')

let ret = sha256(content);
console.log(ret);//64位十六进制 = 256位二进制

HTTPS 过程大致如下:

​ 1) SSL客户端通过TCP和服务器建立连接之后(443端口),并且在一般的tcp连接协商(握手)过程中请求证书。

​ 即客户端发出一个消息给服务器,这个消息里面包含了自己可实现的算法列表和其它一些需要的消息,SSL的服务器端会回应一个数据包,这里面确定了这次通信所需要的算法,然后服务器向客户端返回证书。(证书里面包含了服务器信息:域名。申请证书的公司,公共秘钥)。

​ 2)Client在收到服务器返回的证书后,判断签发这个证书的公共签发机构,并使用这个机构的公共秘钥确认签名是否有效,客户端还会确保证书中列出的域名就是它正在连接的域名。

​ 3) 如果确认证书有效,那么生成对称秘钥并使用服务器的公共秘钥进行加密。然后发送给服务器,服务器使用它的私钥对它进行解密,这样两台计算机可以开始进行对称加密进行通信。

ps:

SSL:安全套接层,是netscape公司设计的主要用于web的安全传输协议。这种协议在WEB上获得了广泛的应用。通过证书认证来确保客户端和网站服务器之间的通信数据是加密安全的。

传输层安全性协议(Transport Layer Security,缩写TLS) 。

跨域

当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。 跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。

可查看: juejin.cn/post/684490…

HTTP CSP(内容安全策略)

内容安全策略 (CSP, Content Security Policy) 是一个附加的安全层,用于帮助检测和缓解某些类型的攻击,包括跨站脚本 (XSS) 和数据注入等攻击。 这些攻击可用于实现从数据窃取到网站破坏或作为恶意软件分发版本等用途。

语法格式:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:

支持的策略指令:

1、 default-src

default-src 指令定义了那些没有被更精确指令指定的(默认)安全策略。该指令包含了以下指令:

  • child-src
  • connect-src
  • font-src
  • img-src
  • media-src
  • object-src
  • script-src
  • style-src

内容源:

内容源有三种:源列表、关键字和数据

关键字:

'none' 代表空集;即不匹配任何 URL。两侧单引号是必须的。

'self' 代表和文档同源,包括相同的 URL 协议和端口号。两侧单引号是必须的。

'unsafe-inline' 允许使用内联资源,如内联的<script>元素、javascript: URL、内联的事件处理函数和内联的<style>元素,两侧单引号是必须的。

'unsafe-eval' 允许使用 eval() 等通过字符串创建代码的方法。两侧单引号是必须的。

代码例子

网站管理员希望所有内容都来自网站本身(不包括子域名)。

Content-Security-Policy: default-src 'self'

网站管理员希望允许来自受信任域及其所有子域的内容(它不必与CSP设置的域相同)。

Content-Security-Policy: default-src 'self' *.a.com

网站管理员希望允许Web应用程序的用户将来自任何来源的图像包含在自己的内容中,但要将音频或视频媒体限制为可信任的提供者,并且仅将所有脚本限制在承载受信任代码的特定服务器上。

Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com

在此,默认情况下,仅允许来自文档来源的内容,但以下情况除外:

  • 图像可能从任何地方加载(请注意“*”通配符)。

  • 媒体只允许来自media1.com和media2.com(而不是来自这些网站的子域)。

  • 可执行脚本只允许来自userscripts.example.com。

更多内容可参考: developer.mozilla.org/zh-CN/docs/…

web攻击

防御这些劫持最好的方法还是从后端入手,前端能做的实在太少。而且由于源码的暴露,攻击者很容易绕过我们的防御手段。但是这不代表我们去了解这块的相关知识是没意义的,本文的许多方法,用在其他方面也是大有作用。

HTTP为什么不安全?有什么缺点?

通信使用明文(不加密),内容可能会被窃听

不验证通信方身份,因此有可能遭遇伪装

无法证明报文的完整性,所以有可能已遭篡改

1、可能被窃听

  • HTTP 本身不具备加密的功能,所以也无法做到通信整体(使用HTTP协议通信的请求和响应的内容)进行加密。HTTP 报文使用明文方式发送。
  • 由于互联网是由联通世界各个地方的网络设施组成,所有发送和接收经过某些设备的数据都可能被截获或窥视。(例如大家都熟悉的抓包工具:Wireshark),即使经过加密处理,也会被窥视是通信内容,只是可能很难或者无法破解出报文的信息而已

2、 认证问题

  • 无法确认你发送到的服务器就是真正的目标服务器(可能服务器是伪装的)
  • 无法确定返回的客户端是否是按照真实意图接收的客户端(可能是伪装的客户端)
  • 无法确定正在通信的对方是否具备访问权限,Web 服务器上某些重要的信息,只想发给特定用户即使是无意义的请求也会照单全收。无法阻止海量请求下的 DoS 攻击(Denial of Service,拒绝服务攻击)。

3、 可能被篡改

请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击被称为中间人攻击(Man-in-the-Middle attack,MITM)。

HTTPS解决上述三个问题

HTTPS基于HTTP协议,通过SSL或TLS(可以看作SSL3.0)提供加密处理数据、验证对方身份以及数据完整性保护。特点如下:

  • 内容加密:采用混合加密技术,中间者无法直接查看明文内容
  • 验证身份:通过证书认证客户端访问的是自己的服务器
  • 保护数据完整性:防止传输的内容被中间人冒充或者篡改

HTTP协议Content Lenth限制漏洞导致拒绝服务攻击

使用POST方法时,可以设置ContentLenth来定义需要传送的数据长度,例如ContentLenth:999999999,在传送完成前,内 存不会释放,攻击者可以利用这个缺陷,连续向WEB服务器发送垃圾数据直至WEB服务器内存耗尽。这种攻击方法基本不会留下痕迹。

利用HTTP协议的特性进行拒绝服务攻击的一些构思

服务器端忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应,这种情况我们称作:服务器端受到了SYNFlood攻击(SYN洪水攻击)。

而Smurf、TearDrop等是利用ICMP报文来Flood和IP碎片攻击的。本文用“正常连接”的方法来产生拒绝服务攻击。

19端口在早期已经有人用来做Chargen攻击了,即Chargen_Denial_of_Service,但是!他们用的方法是在两台Chargen 服务器之间产生UDP连接,让服务器处理过多信息而DOWN掉,那么,干掉一台WEB服务器的条件就必须有2个:1.有Chargen服务2.有HTTP 服务

方法:攻击者伪造源IP给N台Chargen发送连接请求(Connect),Chargen接收到连接后就会返回每秒72字节的字符流(实际上根据网络实际情况,这个速度更快)给服务器。

HTTP头注入

替换HTTP头字符值中的换行符。

CSRF 攻击

CSRF中文名为跨站请求伪造。假如http://a.com网址上有个加关注的GET接口,id参数是关注人Id,如下:

http://a.com?id=12

那我只需要在我的一个页面里面写一个img标签:

<img src="http://a.com?id=12" />

那么只要有已经登录http://a.com网址的用户打开我这个页面,就会自动关注我。 就算是改为POST请求,也可以通过在页面使用form表单提交的方式自动关注。 CSRF攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。CSRF攻击的问题一般是由服务端解决,防范 CSRF 攻击可以遵循以下几种规则:

  1. Get 请求不用于对数据进行修改
  2. Cookie设置HTTP Only
  3. 接口设置禁止跨域
  4. 请求时附带验证信息,比如验证码或者 Token

CSRF 攻击的防范

1、验证码, 验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。

2、Referer Check, 根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。通过 Referer Check,可以检查请求是否来自合法的"源"。 Referer Check 不仅能防范 CSRF 攻击,另一个应用场景是 "防止图片盗链"。

我们可以创建一个白名单记录我们的请求网址,当浏览器器发出请求的referer不在这个白名单内的网址,就认为是收到了csrf攻击,是不合法的请求,要拒绝该请求。

服务端代码:

if (req.headers.referer !== 'http://www.c.com:8002/') {
    res.write('csrf 攻击');
    return;
}

3、添加 token 验证

要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

XSS攻击

XSS跨站脚本(Cross-site scripting) ,指的是攻击者利用漏洞,向 Web 页面中注入恶意代码,当用户浏览该页之时,注入的代码会被执行,从而达到攻击的特殊目的。

XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM。

常见的注入方式

<a href="javascript:alert(1)"></a>
<iframe src="javascript:alert(1)" />
<img src='x' onerror="alert(1)" />
<video src='x' onerror="alert(1)" ></video>
<div onclick="alert(1)" onmouseover="alert(2)" ><div>

XSS 攻击的防范

现在主流的浏览器内置了防范 XSS 的措施,例如 CSP。但对于开发者来说,也应该寻找可靠的解决方案来防止 XSS 攻击。

1、HttpOnly 防止劫取 Cookie:

response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly")

2、输入检查,因为用户可能输入的内容就是注入的脚本,当保存到服务器后,在返显示到页面就会被执行之前用户输入的注入内容;

解决方案: 对于用户的任何输入要进行检查、过滤和转义。建立可信任的字符和 HTML 标签白名单,对于不在白名单之列的字符或者标签进行过滤或编码。

在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <> 等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。

// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&amp;': '&',
  '&#10;': '\n'
}

3、输出检查, 服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

防止文件注入型攻击

防范:

1、文件上传目录设置成不可执行

2、判断文件类型。结合MIME type与文件扩展名,设置文件类型白名单。对于图片文件,可以利用图片库函数深层次检查是否真是图片。

3、重命名文件名。

4、文件服务器使用独立的域名。

SQL注入

防范:数据与代码分离,即不用字符串拼凑SQL语句,使用SQL预处理方法(参数使用占位符 ?)。

XST处理

XST(跨站追踪)攻击,防范:关闭Web 服务器的TRACE方法。

问题回答总结

简述TCP三次握手

  • 第一次握手: 建立连接。客户端发送连接请求,发送SYN报文,将seq设置为X(某个值)。然后,客户端进入SYN_SEND状态,等待服务器的确认。
  • 第二次握手: 服务器收到客户端的SYN报文段。需要对这个SYN报文段进行确认,发送ACK报文,将ack设置为X+1。同时,自己还要发送SYN请求信息,将seq为Y。服务器端将上述所有信息一并发送给客户端,此时服务器进入SYN_RECV状态。
  • 第三次握手: 客户端收到服务器的ACK和SYN报文后,进行确认,然后将ack设置为Y+1,seq设置为X+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

第三次握手的原因是为了防止已失效的连接请求报文段突然又传回给服务器进程从而产生错误。假设客户端发送第一次连接请求由于网络滞留了,于是客户端又发送了一次请求并成功建立连接,数据传输完毕后就释放了连接。在释放连接后的某个时间段内客户端的第一次报文段又到达了服务器并被服务器进程确认,如果没有第三次握手,则服务器会一直等待客户端发送数据,从而浪费许多资源。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

从输入URL到页面加载完成,发生了什么?

可参考:how browers work

1、用户输入URL;

2、 浏览器首先依次查询自身缓存,系统缓存和路由器缓存的记录,如果没有则查询本地host文件,还没有就向DNS服务器发送域名解析请求;

3、浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;

4、解析出 IP 地址后,根据该 IP 地址和默认端口 80(如果输入的URL中有端口号就用你输入的), 向服务器指定端口发起TCP连接请求,通过运输层,网络层,数据链路层,物理层到达服务器,经过三次握手后建立TCP连接 ;

5、三次握手后,就可以传输数据了,客户端将要发送的内容构建成HTTP请求报文并封装在TCP包中,通过TCP协议发送到服务器指定端口;

6、 服务器解析HTTP请求并按照报文格式封装成需要的HTTP对象,返回给浏览器。

7、数据传输完毕后,就通过四次分手将客户端和服务器的连接释放。

8、 浏览器根据拿到的响应报文进行页面的解析和渲染

ps:浏览器解析和渲染

浏览器根据拿到的响应报文进行解析和页面的渲染。 在渲染页面之前,需要构建DOM树和CSSOM树。 浏览器是一个边解析边渲染的过程。

1、构建DOM树

HTML文档会被解析成一棵以document为根的DOM树,解析过程中如果遇到JavaScript,则会暂停解析并传输下载相应的文件造成阻塞,故推荐将JavaScript脚本放在HTML文件的后面。

2、构建CSSSOM树

浏览器根据外部样式,内部样式和内联样式来解析CSS,构建CSSSOM树。

3、构建渲染树和布局

DOM树和CSSOM树构建完毕后会融合成渲染树,然后浏览器会确认页面各元素的位置。

4、页面绘制和优化

浏览器根据布局结果进行页面的绘制,并优化页面内容,减小CPU消耗。

渲染结束后整个页面就呈现在我们面前了。

如何实现浏览器内多个标签页之间的通信?

1、WebSocket:因为websokect是全双工通信,所以可以实现多个标签页之前的通信;

2、SharedWorker: html5浏览器的新特性, 可以多个标签页、iframe共同使用的 3、 localstorage:是浏览器多个标签共用的存储空间,所以可以用来实现多标签之间的通信;

思路: onstorage以及storage事件,针对都是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数。

window.onstorage = (e) => {console.log(e)}

或者

window.addEventListener("storage",function(event){
   &emsp;$("#name").val(event.key+”=”+event.newValue);
}); 

注意quirks:Safari 在无痕模式下设置localstorge值时会抛出 QuotaExceededError 的异常

4、 使用cookie+setInterval

这个方法使用定时器不断栓下,是相当浪费资源的,虽然能实现方案,但是不够优雅。

思路:

在页面A设置一个使用 setInterval 定时器不断刷新,检查 Cookies 的值是否发生变化,如果变化就进行刷新的操作。

由于 Cookies 是在同域可读的,所以在页面 B 审核的时候改变 Cookies 的值,页面 A 自然是可以拿到的。

TCP与UDP的区别

TCP提供面向连接的,可靠的数据传输服务。以报文段的形式传输;

UDP提供无连接的,尽最大努力的数据传输服务。以用户数据报的形式传输。

TCP与UDP的区别: TCP协议相对于UDP协议的特点是:TCP协议提供面向连接、字节流和可靠的传输。

HTTP响应码你都知道哪些?都是什么意思?

常见状态码

    200 - OK 请求成功,客户端发过来的数据被正常处理
    
    204 - Not Content 正常响应,没有实体
    
    206 - Partial Content 范围请求,返回部分数据,响应报文中由Content-Range指定实体内容

    301 - Moved Permanently 请求永久重定向,转移到其它URL

    302 - Found 请求临时重定向
    
    303 - See Other 和302类似,但必须用GET方法

    304 - Not Modified) 状态未改变, 配合(If-Match、If-Modified-Since、If-None_Match、If-Range、If-Unmodified-Since) 
    
    307 - Temporary Redirect 临时重定向,和302类似,不该改变原请求方法

    400 - Bad Request 客户端请求报文存在语法错误

    401 - unauthorized 客户端请求没有经过授权

    403 - Forbidden 客户端的请求被服务器拒绝,一般为客户端没有访问权限

    404 - Not Found 服务器上无法找到请求的资源

    500 - Internal Server Error 服务器内部错误
    
    503 - Service Unavailable 服务器处于超负载或正在停机维护

HTTP协议的工作流程?

  • 1、地址解析

  • 2、封装HTTP请求数据包

  • 3、封装TCP包,建立TCP链接(TCP的三次握手)

  • 4、客户端发送请求命令

  • 5、服务器响应

  • 6、服务器关闭TCP链接

什么是长链接,为什么需要长连接?

长连接也可以成为持久链接,HTTP/1.1默认开启长连接(持久链接), 数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据(一个TCP连接上可以传送多个HTTP请求和响应);

为什么需要长连接?

TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。 随着网页加载的外部资源越来越多,这个问题就愈发突出了。

所以, HTTP 1.0版本的性能比较差。 解决方法:在发送请求时,设置字段:

Connection: keep-alive

这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。 一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

而HTTP/1.1默认开启的, 不用声明Connection: keep-alive

HTTP/2的信道复用又为什么能提高性能?

在 HTTP/2 中,四大特性:头部压缩、二进制流、服务器推送、多路复用。

为什么说HTTP /2 的信道复用能提高性能呢?

  • 同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,这样整个页面资源的下载过程只需要一次慢启动,同时也避免了多个TCP连接竞争带宽所带来的问题。
  • 并行交错地发送多个请求/响应,请求/响应之间互不影响。
  • 在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。

针对HTTP/1.1的优化有哪些HTTP/2不在适用?

  • JS文件的合并。我们现在优化的一个主要方向就是尽量的减少HTTP的请求数, 对我们工程中的代码,研发时分模块开发,上线时我们会把所有的代码进行压缩合并,合并成一个文件,这样不管多少模块,都请求一个文件,减少了HTTP的请求数。但是这样做有一个非常严重的问题:文件的缓存。当我们有100个模块时,有一个模块改了东西,按照之前的方式,整个文件浏览器都需要重新下载,不能被缓存。现在我们有了HTTP/2了,模块就可以单独的压缩上线,而不影响其他没有修改的模块。
  • 多域名提高浏览器的下载速度。之前我们有一个优化就是把css文件和js文件放到2个域名下面,这样浏览器就可以对这两个类型的文件进行同时下载,避免了浏览器6个通道的限制,这样做的缺点也是明显的,1.DNS的解析时间会变长。2.增加了服务器的压力。有了HTTP/2之后,根据上面讲的原理,我们就不用这么搞了,成本会更低。

HTTP1.0 和 1.1 现存的一些问题

  1. HTTP1.x 在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出。
  2. HTTP1.x 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。
  3. HTTP1.x 在使用时,header 里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求 header 基本不怎么变化,尤其在移动端增加用户流量。
  4. 虽然 HTTP1.x 支持了 keep-alive,来弥补多次创建连接产生的延迟,但是 keep-alive 使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务 (例如图片存放网站),keep-alive 可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。

HTTPS和HTTP的区别

  1. HTTPS协议需要到CA(证书颁发机构)申请证书,一般免费证书很少,需要交费。
  2. HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
  3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由HTTP+SSL协议构建的可进行加密传输、身份认证的网络协议,可以有效的防止运营商劫持,解决了防劫持的一个大问题,比http协议安全。

如何将HTTP变为HTTPS?

如果一个网站要全站由 HTTP 替换成 HTTPS,可能需要关注以下几点:

  1. 安装 CA 证书,一般的证书都是需要收费的,
  2. 在购买证书之后,在证书提供的网站上配置自己的域名,将证书下载下来之后,配置自己的 web 服务器,同时进行代码改造。
  3. HTTPS 降低用户访问速度。SSL 握手,HTTPS 对速度会有一定程度的降低,但是只要经过合理优化和部署,HTTPS 对速度的影响完全可以接受。在很多场景下,HTTPS 速度完全不逊于 HTTP,如果使用 SPDY,HTTPS 的速度甚至还要比 HTTP 快。
  4. 相对于 HTTPS 降低访问速度,其实更需要关心的是服务器端的 CPU 压力,HTTPS 中大量的密钥算法计算,会消耗大量的 CPU 资源,只有足够的优化,HTTPS 的机器成本才不会明显增加。

什么是缓存?

缓存HTTP的仓库,使常用页面的副本可以保存在离客户端更近的地方。

HTTP的缓存机制

HTTP的缓存分为强缓存和协商缓存(对比缓存)。

HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,可将其分为强制缓存,对比缓存。

  • 强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互
  • 两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则

如何高效利用缓存,上线前端代码?

1、缓存时间过长,发布上线了,用户端还用缓存,会有bug

2、缓存时间过短,重复加载文件过多,浪费带宽

一般我的处理方式是:

step1:我们不让 html 文件缓存( 静态资源(css、js 、image、audio等) ),每次访问 html 都去请求服务器。所以浏览器每次都能拿到最新的html资源。

step2:将 静态资源(css、js 、image、audio等)原来的文件名加上每次打包的版本号,或者时间戳、指纹,这样的好处是,可以知道静态资源时哪次打包的,哪些资源时这次更改的,如果出现错误,需要回溯版本,也可以快速回溯。其实这样还是不是最优的方法;

对于大公司的静态资源优化方案是: 用文件的摘要信息来对资源文件进行重命名,把摘要信息放到资源文件发布路径中,这样,内容有修改的资源就变成了一个新的文件发布到线上,不会覆盖已有的资源文件。上线过程中,先全量部署静态资源,再灰度部署页面,整个问题就比较完美的解决了。

所以,大公司的静态资源优化方案,基本上要实现这么几个东西:

配置超长时间的本地缓存 —— 节省带宽,提高性能 采用内容摘要作为缓存更新依据 —— 精确的缓存控制 静态资源CDN部署 —— 优化网络请求 更资源发布路径实现非覆盖式发布 —— 平滑升级

step3:先上线静态资源,在上线html

我们可以利用webpack工程化工具解决。

HTTP 如何处理大文件的传输?

HTTP针对大文件的传输场景,可以使用范围请求来解决,客户端将资源一部分一部分传输。

一般是:压缩、分块、范围请求、多端数据的流程。

那范围请求是怎么处理的呢?

响应头使用:Accept-Ranges 用来告知客户端这边是支持范围请求的;

请求头使用: Rang来制定请求哪一部分。Range: bytes=x-y

bytes=x-y 表示范围格式有:

  • 0-y表示从开始到第 y 个字节。
  • y- 表示从第 y 字节到文件终点。
  • -y表示文件的最后y个字节。
  • x-y 表示文件第x-y字节范围的内容

服务器收到请求之后,首先验证范围是否合法,如果越界了那么返回416错误码,否则读取相应片段,返回206状态码。

同时,服务器需要添加Content-Range字段,这个字段的格式根据请求头中Range字段的不同而有所差异。

对于单段数据的请求,返回的响应如下:

HTTP/1.1 206 Partial Content
Content-Length: 10
Accept-Ranges: bytes
Content-Range: bytes 0-9/100

。。。

值得注意的是Content-Range字段,0-9表示请求的返回,100表示资源的总大小,很好理解。

多段数据

接下来我们看看多段请求的情况。得到的响应会是下面这个形式:

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=00000010101
Content-Length: 189
Connection: keep-alive
Accept-Ranges: bytes


--00000010101
Content-Type: text/plain
Content-Range: bytes 0-9/96


--00000010101
Content-Type: text/plain
Content-Range: bytes 20-29/96

eex jspy e
--00000010101--

这个时候出现了一个非常关键的字段Content-Type: multipart/byteranges;boundary=00000010101,它代表了信息量是这样的:

  • 请求一定是多段数据请求
  • 响应体中的分隔符是 00000010101

因此,在响应体中各段数据之间会由这里指定的分隔符分开,而且在最后的分隔末尾添上--表示结束。

什么是代理?

代理是位于客户端和服务器之间的HTTP中间实体。接收所有客户端的HTTP请求,并将这些请求转发给服务器(可能会对请求进行修改之后转发)。

什么是Agent代理?

用户Agent代理是代表用户发起HTTP的客户端程序。比如Web浏览器。另外有些自动发送HTTP请求并获取内容的代理,比如“网络蜘蛛”或者“Web机器人”。

如何理解 HTTP 代理?

在web中,http代理是一种客户端和web服务器之间的一种实体。它既具备客户端的发起请求功能,还可以像web服务器一样返回响应。

代理和网关之间的首要差异是代理只能转发同一种协议,而网关能够转发多种协议。

HTTP 代理存在两种形式,分别简单介绍如下:

第一种是 RFC 7230 - HTTP/1.1: Message Syntax and Routing(即修订后的 RFC 2616,HTTP/1.1 协议的第一部分)描述的普通代理。这种代理扮演的是「中间人」角色,对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP 报文。

第二种是 Tunneling TCP based protocols through Web proxy servers(通过 Web 代理服务器用隧道方式传输基于 TCP 的协议)描述的隧道代理。它通过 HTTP 协议正文部分(Body)完成通讯,以 HTTP 的方式实现任意基于 TCP 的应用层协议代理。这种代理使用 HTTP 的 CONNECT 方法建立连接,但 CONNECT 最开始并不是 RFC 2616 - HTTP/1.1 的一部分,直到 2014 年发布的 HTTP/1.1 修订版中,才增加了对 CONNECT 及隧道代理的描述,详见 RFC 7231 - HTTP/1.1: Semantics and Content。实际上这种代理早就被广泛实现。

什么是网关?

网关是一种特殊的服务器,作为其他服务器的中间实体使用。通常用于将HTTP流量转换成其他的协议。

什么是隧道?

隧道是建立起来之后,就会在两条连接之间对原始数据进行盲转发的HTTP应用程序。常见用途是通过HTTP连接承载加密的安全套接字层(SSL)流量,这样SSL流量就可以穿过只允许Web流量通过的防火墙了。

———————————————————————————————————

本文解析或者答案仅供参考,内容参考以下资料~~

参考资料:

《图解HTTP》

《HTTP权威指南》

taligarsiel.com/Projects/ho…

cloud.tencent.com/document/pr…

httpwg.org/specs/rfc75…

www.runoob.com/http/http-c…

ye11ow.gitbooks.io/http2-expla…

github.com/creeperyang…

www.cnblogs.com/coco1s/p/57…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…