Object Oriented Programming

面向对象编程 (Object Oriented Programming)

面向对象编程是一种基于 对象 概念的编程范例,对象可以以字段的形式包含数据(通常被称为属性),以程序的形式包含代码(通常被称为方法)。

对象的一个特点就是它的方法可以访问和修改与之关联的对象的数据字段。在面向对象编程中,计算机程序被设计为由能够互相交互的对象们组成。面向对象编程语言是多种多样,最受欢迎的是基于类(Class)的那些。

面向对象编程的特征

通常,面向对象编程被理解为一种将程序分解为封装数据及相关操作的模块的编程范例。它的特征如下:

  • 类与对象

    支持面向对象编程的语言通常使用继承类来达到代码重用或可扩展性。

    • 类 :定义了事物的抽象特点
    • 对象 :类的实例
  • 动态配置( dynamic dispatch )与消息传递机制

    对象的任务是通过选择执行合适的程序代码响应方法调用,通常这种选择是在运行时查找与对象关联的表中的方法来实现的。这被称之为动态分配,它将对象与抽象数据类型(或模块)区分开来,后者具有所有实例的操作静态实现。

    方法调用也被称之为消息传递。它被概念化为传递给对象进行分配的消息(即方法的名称和输入的参数)。

  • 封装

    封装是面向对象编程的一个概念,它将数据和对数据的操作绑定在一起,并保护它们免受外部的干扰和误用(仅让允许的类和对象进行访问)。数据封装引入了重要的面向对象编程数据隐藏概念。

    如果一个类不允许调用代码直接访问对象内部的数据,而只允许通过它提供的方法进行访问,这种强大的抽象或信息隐藏被称之为封装。

    具备封装性的面向对象编程隐藏了某一方法对的具体执行过程,取而代之的是通过消息传递机制传递消息给它。

  • 继承

    几乎所有支持类的语言都支持继承。继承使得类能够以层级结构表示 is-a-type-of 的关系。父类的所有数据和方法都能被子类以同样的名字所继承,这使得相同的代码和数据复用变得更加简单,而且还是镜像真实世界关系的一种直观方式。

    子类可以重写父类定义的方法。在一些语言中存在多重继承,这可能会造成重写冲突。

    抽象类不能实例化为对象,它们的存在只是为了可以被继承到其它可以被实例化的具体类中。

    继承的过程就是一个从一般到特殊的过程。

  • 多态

    多态是指有继承产生的相关但不同的类,对统一消息会做出不同的响应。它能极大减少类型之间的耦合。

  • 抽象

JavaScript 与面向对象编程

JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的 OOP 语言能力。

命名空间

命名空间是一个容器,它允许开发人员在一个独特的、特定于应用程序的名称下捆绑所有功能。在 JavaScript 中,命名空间是一个包含方法、属性和对象的对象,它和普通对象在语言层面上并无差别。

使用命名空间可以最大程度上减少应用程序名称冲突的可能性。

自定义对象

JavaScript 是基于原型的语言,并没有声明类的语句。它使用函数做类,定义一个类和定义一个函数相同。

1
2
3
function Person() {}
// or
let Person = function() {}
对象

对象是类的实例。

1
2
3
4
function Person() {}
// 创建为初始化的实例
let person1 = new Person();
let person2 = Object.create(Person)
构造器

构造器在实例化时被调用,它是对象中的一个方法,常用于给对象的属性赋值或者为调用函数做准备。在 JavaScript 中函数就可以作为构造器使用,因此不需要特别地定义一个构造器方法,每个声明的函数都可以在实例化后被调用执行。

属性

属性就是类中包含的变量。每一个对象实例有若干个属性. 为了正确的继承,属性应该被定义在类的原型属性 (函数)中。

可以使用 this 关键字调用类的属性,它是对当前对象的引用。

1
2
3
4
5
function Person(name) {
this.name = name;
}

let person = new Person('Li Lei');
方法

在JavaScript中方法通常是一个绑定到对象中的普通函数, 这意味着方法可以在其所在上下文之外被调用。

1
2
3
4
5
6
7
function Person(name) {
this.name = name;
}

Person.prototype.sayHello = function() {
console.log('My name is ' + this.anme);
}

在方法调用的过程中,this 的值取决于我们如何调用方法。可以通过 Function#callFuncation#apply 显式的指定 this 的值。

继承

在 Javascript 中,继承通过赋予子类一个父类的实例并专门化子类来实现。在现代浏览器中你可以使用 Object.create() 实现继承。

1
2
3
4
5
6
7
8
function Person() {}

function Student() {
Person.call(this);
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
封装

类的实例调用存在于类中的方法时,并不清楚它是如何实现并运行的,这就是封装。

抽象

抽象是允许模拟工作问题中通用部分的一种机制。这可以通过继承(具体化)或组合来实现。
JavaScript通过继承实现具体化,通过让类的实例是其他对象的属性值来实现组合。

多态

就像所有定义在原型属性内部的方法和属性一样,不同的类可以定义具有相同名称的方法;方法是作用于所在的类中。并且这仅在两个类不是父子关系时成立(继承链中,一个类不是继承自其他类)。

缓存

强制缓存

强缓存策略在请求数据时,如果浏览器缓存中存在未失效的缓存数据,则直接从缓存中获取数据,不与服务器进行交互。只有在缓存中不存在要请求的数据或在缓存中的数据失效时,才会从服务器获取数据。

强制缓存01

强缓存由 Expires/Cache-control/Pragma 三个 Header 属性进行控制。

  • Expires

    Expires 的值是一个 HTTP 日期,表示资源的过期时间。

    在发起请求时,将 Expires 日期与系统时间进行对比,如果系统时间超过了 Expires 日期,则认为资源过期失效。

    但由于系统时间和服务器时间可能不一致,会造成判断不准的问题。

    Expires 在三个强缓存控制属性中优先级最低

  • Cache-control

    Cache-control 是 HTTP/1.1 中新增的特性,在请求头和响应头中都能使用,可用值如下

    • max-age

      max-age 的值是一个秒数,表示从起发起时到缓存过期的时间

      max-age=10

    • no-cache

      不使用强缓存,每次请求都需要和服务器验证

    • no-store

      禁止使用缓存(包括协商缓存),每次请求都向服务器请求最新的资源

    • private

      不允许中间代理、CDN等缓存

    • public

      允许中间代理、CDN等缓存

    • must-revalidate

      缓存过期后必须向服务器验证

  • Pragma

    Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高

协商缓存

当浏览器的强缓存失效或在请求头中设置了不使用强缓存,并在请求头中设置了 if-modified-sinceif-none-match 时,会将这两个属性值到服务器验证是否命中协商缓存。

协商缓存01

  • ETag/If-None-Match

    ETag/If-None-Match 的值是一串 hash 码,代表的是一个资源的标识符,当服务端的文件变化的时候,它的 hash码会随之改变,通过请求头中的 If-None-Match 和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。

  • Last-Modified/If-Modified-Since

    Last-Modified/If-Modified-Since 的值代表的是文件的最后修改时间,第一次请求服务端会把资源的最后修改时间放到 Last-Modified 响应头中,第二次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中,服务端根据文件最后一次修改时间和 If-Modified-Since 的值进行比较,如果相等,返回 304 ,并加载浏览器缓存。

缓存用处

  • 减少了冗余的数据传递,节省宽带流量
  • 减少了服务器的负担,大大提高了网站性能
  • 加快了客户端加载网页的速度 这也正是HTTP缓存属于客户端缓存的原因

TCP/IP 协议族

TCP/IP 协议族

TCP/IP 协议不是 TCPIP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。

参考模型

TCP/IP 参考模型

TCP/IP 参考模型是首先由 ARPANET 所使用的网络体系结构。这个体系结构在它的两个主要协议出现以后被称为 TCP/IP 参考模型。这一网络协议一般分为四层:

  • 网络访问层/链路层,用来处理连接网络的硬件部分。

  • 互联网层/网络层用来处理在网络上流动的数据包,是整个体系结构的关键部分,其功能是使主机可以把分组发往任何网络,并使分组独立地传向目标。这些分组可能经过不同的网络,到达的顺序和发送的顺序也可能不同。高层如果需要顺序收发,那就必须自行处理对分组的排序。互联网层使用因特网协议(IP)。

  • 传输层为处于网络连接中的计算机之间的通信提供数据传输服务。在这一层定义了两个端到端的协议:传输控制协议(TCP)和用户数据报协议(UDP)。

    • TCP 是面向连接的协议,它提供可靠的报文传输和对上层应用的连接服务。为此,除了基本的数据传输外,它还有可靠性保证、流量控制、多路复用、优先权和安全性控制等功能。

    • UDP 是面向无连接的不可靠传输协议,主要用于不需要 TCP 的排序和流量控制等功能的应用程序。

  • 应用层包含所有的高层协议,包括:虚拟终端协议(TELNET)、文件传输协议(FPT)、电子邮件传输协议(SMTP)、域名服务(DNS,)、网上新闻传输协议(NNTP)和超文本传输协议(HTTP),这些协议为应用提供对应的通信服务。

    • TELNET 是远程登录服务的标准协议和主要方式,为用户提供了在本地计算机上完成远程主机工作的能力。
      • FTP 是用于在网络上进行文件传输的一套标准协议。
    • SMTP 是一个提供可靠且有效的电子邮件传输的协议,它建立在 FTP 服务之上,主要用于完成系统之间的邮件信息传递,并提供有关来信的通知。
      • DNS 主要用于域名和 IP 之间的相互转换,是一种分布式网络目录服务。
    • NNTP 用于新闻的发布、检索和获取。
      • HTTP 是一个基于 TCP 协议的用于客户端和服务端通信的 请求-响应 协议。

image-20200714091919812

OSI 参考模型

OSI 参考模型是国际标准化组织指定的一个用于计算机或通信系统之间互联的标准体系。

  • 物理层 - 通过物理媒体传输原始字节流

  • 链路层 - 定义网络上数据的格式

  • 网络层 - 决定数据通过哪条物理路径进行传输

  • 传输层 - 通过传输协议传输数据

  • 会话层 - 维护链接并负责控制端口和会话

  • 展示层 - 保证数据的格式时可用并加密的

  • 应用层 - 计算机交互层,在这里应用可以访问网络服务

TCP/IP 特点

  • TCP/IP 协议不依赖于任何特定的计算机硬件或操作系统,提供开放的协议标准。
  • TCP/IP 并不依赖于特定的网络传输硬件,所以 TCP/IP 协议能够集成各种各样的网络。
  • 统一的网络地址分配方案,使整个 TCP/IP 设备在网中都有唯一的地址。
  • 标准化的高层协议,可以提供多种可靠的服务。

TCP/IP 通信传输流

利用 TCP/IP 协议族进行网络通信时, 会通过分层顺序与对方进行通信。发送端从应用层往下走,接收端则往应用层往上走。

发送端在层与层之间传输数据时,每经过一层时必定会被打上一个 该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层 时会把对应的首部消去。

这种把数据信息包装起来的做法称为封装。

image-20200714103627565

IP 协议

IP 协议位于 TCP/IP 参考模型中的网络层,是整个 TCP/IP 协议族的核心,也是构成互联网的基础。它的主要内容包括:

  • IP 编址方案
  • 分组封装格式
  • 分组转发规则

TCP 协议

TCP 协议位于参考模型中的传输层,是一种面向连接的、可靠的、基于字节流的传输层通信协议。当应用层向传输层发送用于网间传输的、用8位字节表示的数据流,TCP 则把数据流分割成适当长度的报文段( segment ),最大传输段大小通常受该计算机连接的网络的数据链路层的最大传送单元( MTU )限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。

为了准确无误地将数据送达目标处,TCP 协议采用了三次握手策略来建立连接:

  1. 发送端首先向接收端发送一个带有 SYN 标志的数据包

  2. 接收端接收到带有 SYN 标志的数据包后,返回带有 ACK/SYN 标志的数据包

  3. 发送端在接收到接收端回传的带有 ACK/SYN 标志的数据包后,发送带有 ACK 标志的数据包表示握手结束

三次握手

连接建立后,就可以开始传输数据了。

TCP 连接其实是接收端和客户端保存的一份关于对方的信息,如果 IP 地址、端口号等。

同样的,在断开连接时,TCP 会采用四次挥手策略来保证接数据发送安全和完整:

  1. 当发送端的数据都传输完成后,发送端会向接收端发送连接释放报文 FIN。需要注意的是发送端发送 FIN 报文后,只是不能发送数据了,但是能正常接收数据的。

  2. 接收端收到发送端的 FIN 报文后,回复包含 ACK 标志的确认报文,此时接收端处于等待关闭状态,而不是马上给发送端发送 FIN 报文,因为可能还有数据没有发送完成。

  3. 接收端的数据发送完成后,发送带有 ACKFIN 标志的连接释放报文给发送端。

  4. 发送端收到接收端的 FIN 报文后,向接收端发送 ACK 报文。此时接收端并不是立刻释放 TCP 连接,而是等待 2MSL( 最大报文段寿命的两倍时长 )后才释放连接。但是接收端一旦收到发送端的 ACK 报文后就会立马释放连接。

四次挥手

TCP 和 UDP

TCP 是面向连接的传输控制协议,而 UDP 提供了无连接的数据报服务;

TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;UDP 在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序,应用程序需要负责传输可靠性方面的所有工作;

UDP 具有较好的实时性,工作效率较 TCP 协议高;

UDP 段结构比 TCP 的段结构简单,因此网络开销也小。

TCP 协议可以保证接收端毫无差错地接收到发送端发出的字节流,为应用程序提供可靠的通信服务。对可靠性要求高的通信系统往往使用 TCP 传输数据。比如 HTTP 运用 TCP 进行数据的传输。

HTTP 概览

HTTP 协议

开发中,我们经常需要向服务器端发送数据或从服务器端请求特定数据,为了完成数据在客户端和服务器端的传输,我们在传输数据时必须用到 HTTP 协议。

什么是 HTTP 协议?

HTTP 协议,即 HyperText Transmission Protocol,超文本传输协议,定义了客户端与服务器端的数据传输规则,让客户端和服务器能够有效地进行数据沟通。

HTTP 的基本性质

  • HTTP 是简单的
  • HTTP 是可扩展的 - 通过 HTTP headers 可以轻松对协议进行扩展
  • HTTP 是无状态,有会话的 - 在同一个连接中,两个执行成功的请求之间是没有关系的

HTTP 请求与响应

HTTP 请求

HTTP 协议规定,一个完整的 HTTP 请求应包含如下内容

  • 请求行 :包含请求方法、请求统一资源标示符和 HTTP 版本号。

  • 请求头 :请求头包含客户端传送给服务器端的附加信息。

    Name Description
    Host 目标服务器的网络地址
    Accept 告知服务器端客户端能够接收的数据类型,如 ‘text/html’等
    Content-Type 请求体中的数据类型,如 ‘Application/Json; charset=UTF-8’等
    Accept-Language 客户端的语言环境,如 ‘zh-cn’ 等
    Accept_Encoding 客户端支持的数据压缩格式,如 ‘gzip’ 等
    User-Agent 客户端的软件环境
    Connection : keep-alive 告知服务器这是一个持久连接
    Content-Length 请求体的长度
    Cookie 记录着用户保存在本地的用户数据
  • 请求体 :发送给服务器端的数据

    在使用 POST-Multipart 上传请求中请求体就是上传文件的二进制数据。

    在使用 GET 请求时,请求体为空。

    在普通的 POST 请求中,请求体就是表单数据。

  • 响应状态行 : 服务器返回给客户端的状态信息,一般包含 HTTP 版本号、状态码和状态码对应的英文名称。

    一个典型的状态行如下:

    1
    HTTP/1.1 200 OK	

HTTP 响应

基本与 HTTP 请求相同。

HTTP 的版本

HTTP 的主要版本如下

Version Feature
< HTTP 1.1 不支持持久连接;无请求头和响应头;客户端的前后请求是同步的。
HTTP 1.1 增加请求头和响应头;支持持久连接;客户端的不同请求之间是异步的。
HTTP 2.0 向下兼容 HTTP 1.1,但只用于 https 网址。

HTTP 缓存

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。这样带来的好处有:缓解服务器端压力,提升性能(获取资源的耗时更短了)。对于网站来说,缓存是达到高性能的重要组成部分。缓存需要合理配置,因为并不是所有资源都是永久不变的:重要的是对一个资源的缓存应截止到其下一次发生改变(即不能缓存过期的资源)。

缓存操作的目标

虽然 HTTP 缓存不是必须的,但重用缓存的资源通常是必要的。然而常见的 HTTP 缓存智能存储 GET 响应。

缓存控制

Cache-control

HTTP/1.1 定义的 Cache-Control 头用来区分对缓存机制的支持情况,请求头和响应头都支持这个属性,你可以通过它提供的不同的值来定义缓存策略。

  • 禁止进行缓存

    1
    2
    Canche-Control: no-store,
    Canche-Control: no-cache, no-store
  • 强制确认缓存

    每次有请求发出时,缓存会将次请求发送到服务器,服务器端会验证请求中所描述的缓存是否过期,若未过期,则缓存才使用本地缓存副本

    1
    Cache-Control: must-revalidate
  • 私有缓存和公共缓存

    public 指令表示该响应可以被任何中间人缓存。若指定了 public ,则一些通常不被中间人缓存的页面,将被缓存。

    private 则表示该响应是专用于某单个用户的,中间人不能缓存此响应。

  • 缓存过期机制

    max-age=<seconds> 指令表示资源能够被缓存的最大时间,这个时间是距离请求发起的时间的秒数。一般用来缓存应用中不会改变的文件,通过手动设置一定的时长以保证缓存有效。

    1
    Cache-Control: max-age=10000
  • 缓存验证确认

    当使用了 must-revalidate 指令,那就意味着缓存在考虑使用一个资源时,必须先验证它的状态,已过期的缓存将不被使用

Pargma

Pargma 是 HTTP/1.1 标准中定义的一个 header 属性,请求中包含 Pargma 的效果跟在头信息中定义 Cache-Control: no-cache 相同,但是 HTTP 的响应头不支持这个属性,所以它不能完全替代 Cache-Control 头。

新鲜度

在过期时间之前,缓存资源是新鲜的,否则是陈旧的。一个陈旧的资源是不会被直接清除的,当客户端发起一个请求时,检索到已经有一个对应的缓存副本,则会在此次请求上附加一个 If-None-Match 头,然后再发送给服务器,以此来检查此资源是否依然是新鲜的,若返回 304 (Not Modified) ,则表示该副本是新鲜的,否则返回新的资源。

缓存验证

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有 Cache-control: must-revalidate 的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置 Advanced->Cache 为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

ETag

作为缓存的一种强校验器,ETag 响应头是一个对用户代理不透明的值。对于像浏览器这样的 HTTP UA,不知道 ETag 代表什么,不能预测它的值是多少。如果资源请求的响应头里含有 ETag, 客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。

Last-Modified 响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。

当向服务端发起缓存校验的请求时,服务端会返回 200 ok 表示返回正常的结果或者 304 Not Modified表示浏览器可以使用本地缓存文件。304 的响应头也可以同时更新缓存文档的过期时间。

需要注意的是 If-None-Match 的优先级高于 If-Modified-Since,两者同时存在的话,按照前者进行校验。

Vary 头的响应

Vary HTTP 响应头决定了对于后续的请求头,如何判断是请求一个新的资源还会使用缓存的文件。

当缓存服务器收到一个请求,只有当前的请求和原始(缓存)的请求头跟缓存的响应头里的Vary都匹配,才能使用缓存的响应。

HTTP Cookies

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

服务器通过在响应头里面添加一个 Set-Cookie 选项,来使浏览器保存下 Cookie,之后对该服务器的每一次请求中都通过 Cookie 请求头部将 Cookie 信息发送给服务器。

  • Set-Cookie 响应头部和 Cookie 请求头部

    服务器使用 Set-Cookie 响应头部向用户代理发送 Cookie 信息

    1
    Set-Cookie: <name>=<value>

    保存 Cookie 信息后,对该服务器发起的每一次新请求,浏览器都会将保存的 Cookie 信息通过 Cookeie 请求头再发送给服务器。

  • 会话期 Cookie

    会话期 Cookie 是最简单的 Cookie:浏览器关闭后它会被自动删除,即它仅在会话期内有效。

    会话期 Cookie 不需要指定过期时间或者有效期

  • 持久性 Cookie

    持久性 Cookie 指定了特定的过期时间或有效期,不会随着浏览器的关闭而被删除。

    1
    2
    Set-Cookie: id=asfwa; 
    Expires=Wed, 21 Oct 2015 07:28:00 GMT;

    设定的过期时间只和客户端有关,而不是服务端。

  • Cookie 的 SecureHttpOnly 标记

    标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务器,但由于 Cookie 固有的不安全性,敏感信息不应该通过 Cookie 传输。

    为避免跨域脚本攻击,通过 JavaScript 的 Document.cookie API 无法访问带有 HttpOnly 标记的 Cookie,它们只应发送给服务器。

  • Cookie 的作用域

    通过 DomainPath 标识可以定义 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

    Domain 指定哪些主机可以接受 Cookie,如果不指定,则默认为当前文档的主机,且不包含子域名。如果指定了,则会包含子域名。

    Path 指定主机下的哪些路径可以接受 Cookie,以字符 %x2F (即 /) 作为路径分隔符,子路径也会被匹配。

  • SameSite Cookies

    SameSite Cookie 允许服务器要求某个 Cookie 在跨站请求时不会被发送,从而可以组织跨站请求伪造攻击。

  • JavaScript 通过 document.cookies 访问 Cookie

    通过 Document.cookie 属性可创建新的 Cookie,也可以通过该属性访问非 HttpOnly 标记的 Cookie。

当机器处于不安全环境时,切记不能通过 Cookie 存储传输敏感信息。

  • 会话劫持和 XSS

    在 Web 应用中, Cookie 常用来标记用户或授权会话。因此,如果 Web 应用的 Cookie 被窃取,可能导致授权用户的会话受到攻击。

    HttpOnly 类型的 Cookie 由于阻止了 JavaScript 对其的访问性能而在一定程度上缓解了此类攻击。

  • 跨站请求伪造

    通过以下方式可以一定程度上阻止宽展请求伪造:

    • 对用户输入进行过滤来阻止 XSS
    • 任何敏感操作都需要确认
    • 用于敏感信息的 Cookie 只能拥有较短的生命周期

追踪和隐私

  • 第三方 Cookie

    每个 Cookie 都会有与之关联的域(Domain),如果 Cookie 的域和页面的域相同,那么我们称这个 Cookie 为第一方 Cookie ,如果 Cookie 的域和页面的域不同,则称之为第三方Cookie。一个页面包含图片或存放在其他域上的资源(如图片广告)时,第一方的 Cookie 也只会发送给设置它们的服务器。通过第三方组件发送的第三方 Cookie 主要用于广告和网络追踪。

  • 禁止追踪 Do-Not-Track

    虽然并没有法律或者技术手段强制要求使用DNT,但是通过DNT可以告诉Web程序不要对用户行为进行追踪或者跨站追踪。

  • 欧盟 Cookie 指令

  • 僵尸 Cookie 和删不掉的 Cookie

HTTP 访问控制

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

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(Preflight Request),从而获知服务端是否允许该跨域请求。服务端确认允许后,才发起实际的 HTTP 请求。

HTTP 消息

HTTP 消息是客户端和服务器之间交换数据的方式。它们分为两种类型:由客户端发送的用来在服务器上触发动作的消息和从服务器得到的回应。

HTTP 消息由跨越多行的用 ASCII 编码的文本信息组成。在 HTTP/1.1 和更早期的版本的协议中,消息通过连接明文发送。在 HTTP/2 中,为了优化和性能提升,人类可读的消息被分割成 HTTP 帧。

HTTP Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 请求头的名称是大小写不敏感的

// Start line
// [HTTP Method] [Request Target] [HTTP Version]
GET /img/me.png HTTP/1.1
GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1

// Headers
// Request Headers : 对请求的设置
User-Agent
Accept
Accept-Language
Accept-Encoding

// General Headers : 作用于消息整体
Connection

// Entity Headers : 作用与请求的 body 部分,如果 body 部分没有数据,则没有这部分头
Content-Type
Content-Length

// Body
// 大体上分为两类 : 单一资源 body 和多资源 body

HTTP Response

1
2
3
4
5
6
7
8
// Status line
// [HTTP Version] [Status Code] [Status Text]
HTTP/1.1 404 Not Found

// Headers
// 与 HTTP Request 结构相似

// Body

HTTP/2 帧

HTTP/1.1 的消息在性能上有着一系列的缺点:

  • Header 不像 body,它不会被压缩
  • 两个报文之间的 header 通常非常相似,但它们仍然在连接中重复传输
  • 无法复用。当在同一个服务器打开几个连接时:TCP 热连接比冷连接更加有效

所以在 HTTP/2 中,消息被分割成嵌入到流中的帧。Headers 和 Body 的帧是分开的,这使得 Headers 帧也可以被压缩。多个流可以被组合在一起,这是一种称为多路复用的技术,它使得 TCP 连接下的传输更有效率。

HTTP 会话

在类似 HTTP 的客户端-服务器协议中,会话由三个部分组成:

  • 客户端建立一个 TCP 连接
  • 客户端发送请求,等待回应
  • 服务器处理请求,做出回应

从 HTTP/1.1 起,连接在完成第三部后不再被关闭,客户端被允许发起新的请求,这意味着第二和第三部可以重复进行多次。

客户端-服务器协议中,在 HTTP 中打开一个连接,意味着在底层传输层初始化连接。使用 TCP 时,HTTP 服务器默认的端口号是 80。

HTTP 、Scoket 和 TCP 的区别

HTTP 是应用层的协议,TCP 是传输层的协议,而 Socket 是从传输层抽象的一个抽象层,本质是接口。

  1. TCP 连接与 HTTP 连接的区别

    HTTP 是基于 TCP的,客户端向服务器端发送一个 HTTP 请求时,第一步就是要建立与服务端的 TCP 连接。

  2. TCP 连接与 Socket 连接的区别

    Socket 层只是在 TCP/UDP 传输层上做的一个抽象接口层。

    基于 TCP 协议的 Socket 连接同样需要通过三次握手建立连接,是可靠的。

    基于 UDP 协议的 Socket 连接不需要建立连接的过程,不管对方能不能收到都会发送过去,是不可靠的。

  3. HTTP 连接与 Socket 连接的区别

    • HTTP 是短连接,基于 TCP 协议的 Socket 连接是长连接。尽管 HTTP 1.1 开始支持持久连接,但仍无法保证始终连接。

      而基于 TCP 协议的 Socket 连接一旦建立成功,除非一方主动断开,否则连接状态一直保持。

    • HTTP 连接,服务器无法主动发送消息,而 Socket 连接,双发请求的发送没有先后限制。

      HTTP 采用 ‘请求-响应’ 机制,在客户端没有发送请求给服务端时,服务端无法推送消息给客服端。

      Socket 连接双方类似于 P2P 的关系,可以随时互相发送消息。

网络编程基础

网络编程

TCP/IP 协议

TCP/IP 协议的基本概念

TCP/IP 协议不是 TCP 和 IP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。

TCP/IP 是 Transmission Control Protocol / Internet Protocol 的简写,即 ‘传输控制协议/因特网互联协议’,又名网络通讯协议,是 Internet 最基本的协议、Internet 国际互联网络的基础。

TCP/IP 由网络层的 IP 协议和传输层的 TCP 协议组成。

TCP/IP 定义了电子设备如何连如因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级架构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言,TCP 负责发现传输的问题,一旦发现问题就发出信号,要求重新传输,直到所有数据正确地传输到目的地。而 IP 是给因特网每一台联网设备规定一个地址。

参考模型

TCP/IP 参考模型

TCP/IP 参考模型是首先由 ARPANET 所使用的网络体系结构。这个体系结构在它的两个主要协议出现以后被称为 TCP/IP 参考模型。这一网络协议共分为四层:

  • 网络访问层,即 Network Access Layer,在 TCP/IP 参考模型中并没有信息描述,只是指出主机必须使用某种协议与网络相连。

  • 互联网层,即 Internet Layer,是整个体系结构的关键部分,其功能是使主机可以把分组发往任何网络,并使分组独立地传向目标。这些分组可能是经过不同的网络,到达的顺序和发送的顺序也可能不同。高层如果需要顺序收发,那就必须自行处理对分组的排序。互联网层使用因特网协议(IP, Internet Protocol)。

  • 传输层,即 Transport Layer,使源端和目的端机器上的对等实体可以进行会话。在这一层定义了两个端到端的协议:传输控制协议(TCP,Transmission Control Protocol)和用户数据报协议(UDP,User Datagram Protocol)。

    TCP 是面向连接的协议,它提供可靠的报文传输和对上层应用的连接服务。为此,除了基本的数据传输外,它还有可靠性保证、流量控制、多路复用、优先权和安全性控制等功能。

    UDP 是面向无连接 的不可靠传输协议,主要用于不需要 TCP 的排序和流量控制等功能的应用程序。

  • 应用层,即 Application Layer,包含所有的高层协议,包括:虚拟终端协议(TELNET, TELecommunications NETwork)、文件传输协议(FPT, File Transfer Protocol)、电子邮件传输协议(SMTP, Simple Mail Transfer Protocol)、域名服务(DNS, Domain Name Service)、网上新闻传输协议(NNTP, Net News Transfer Protocol)和超文本传输协议(HTTP, HyperText Transfer Protocol)。

    TELNET 允许一台机器上的用户登录到远程机器上,并进行工作。

    FTP 提供了有效地将文件从一台机器上转移到另一台机器上的方法。

    SMTP 用于电子邮件的收发。

    DNS 用于把主机名映射到网络地址。

    NNTP 用于新闻的发布、检索和获取。

    HTTP 用于在 WWW 上获取网页。

OSI 参考模型

OSI 参考模型是国际标准化组织指定的一个用于计算机或通信系统之间互联的标准体系。

  • 物理层 - 通过物理媒体传输原始字节流

  • 链路层 - 定义网络上数据的格式

  • 网络层 - 决定数据通过哪条物理路径进行传输

  • 传输层 - 通过传输协议传输数据

  • 会话层 - 维护链接并负责控制端口和会话

  • 展示层 - 保证数据的格式时可用并加密的

  • 应用层 - 计算机交互层,在这里应用可以访问网络服务

TCP/IP 特点

  • TCP/IP 协议不依赖于任何特定的计算机硬件或操作系统,提供开放的协议标准。
  • TCP/IP 并不依赖于特定的网络传输硬件,所以 TCP/IP 协议能够集成各种各样的网络。
  • 统一的网络地址分配方案,使整个 TCP/IP 设备在网中都有唯一的地址。
  • 标准化的高层协议,可以提供多种可靠的服务。

HTTP 协议

开发中,我们经常需要向服务器端发送数据或从服务器端请求特定数据,为了完成数据在客户端和服务器端的传输,我们在传输数据时必须用到 HTTP 协议。

什么是 HTTP 协议?

HTTP 协议,即 HyperText Transmission Protocol,超文本传输协议,定义了客户端与服务器端的数据传输规则,让客户端和服务器能够有效地进行数据沟通。

HTTP 的基本性质

  • HTTP 是简单的
  • HTTP 是可扩展的 - 通过 HTTP headers 可以轻松对协议进行扩展
  • HTTP 是无状态,有会话的 - 在同一个连接中,两个执行成功的请求之间是没有关系的

HTTP 请求与响应

HTTP 请求

HTTP 协议规定,一个完整的 HTTP 请求应包含如下内容

  • 请求行 :包含请求方法、请求统一资源标示符和 HTTP 版本号。

  • 请求头 :请求头包含客户端传送给服务器端的附加信息。

    Name Description
    Host 目标服务器的网络地址
    Accept 告知服务器端客户端能够接收的数据类型,如 ‘text/html’等
    Content-Type 请求体中的数据类型,如 ‘Application/Json; charset=UTF-8’等
    Accept-Language 客户端的语言环境,如 ‘zh-cn’ 等
    Accept_Encoding 客户端支持的数据压缩格式,如 ‘gzip’ 等
    User-Agent 客户端的软件环境
    Connection : keep-alive 告知服务器这是一个持久连接
    Content-Length 请求体的长度
    Cookie 记录着用户保存在本地的用户数据
  • 请求体 :发送给服务器端的数据

    在使用 POST-Multipart 上传请求中请求体就是上传文件的二进制数据。

    在使用 GET 请求时,请求体为空。

    在普通的 POST 请求中,请求体就是表单数据。

  • 响应状态行 : 服务器返回给客户端的状态信息,一般包含 HTTP 版本号、状态码和状态码对应的英文名称。

    一个典型的状态行如下:

    1
    HTTP/1.1 200 OK	

HTTP 响应

基本与 HTTP 请求相同。

HTTP 的版本

HTTP 的主要版本如下

Version Feature
< HTTP 1.1 不支持持久连接;无请求头和响应头;客户端的前后请求是同步的。
HTTP 1.1 增加请求头和响应头;支持持久连接;客户端的不同请求之间是异步的。
HTTP 2.0 向下兼容 HTTP 1.1,但只用于 https 网址。

HTTP 缓存

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。这样带来的好处有:缓解服务器端压力,提升性能(获取资源的耗时更短了)。对于网站来说,缓存是达到高性能的重要组成部分。缓存需要合理配置,因为并不是所有资源都是永久不变的:重要的是对一个资源的缓存应截止到其下一次发生改变(即不能缓存过期的资源)。

缓存操作的目标

虽然 HTTP 缓存不是必须的,但重用缓存的资源通常是必要的。然而常见的 HTTP 缓存智能存储 GET 响应。

缓存控制

Cache-control

HTTP/1.1 定义的 Cache-Control 头用来区分对缓存机制的支持情况,请求头和响应头都支持这个属性,你可以通过它提供的不同的值来定义缓存策略。

  • 禁止进行缓存

    1
    2
    Canche-Control: no-store,
    Canche-Control: no-cache, no-store
  • 强制确认缓存

    每次有请求发出时,缓存会将次请求发送到服务器,服务器端会验证请求中所描述的缓存是否过期,若未过期,则缓存才使用本地缓存副本

    1
    Cache-Control: must-revalidate
  • 私有缓存和公共缓存

    public 指令表示该响应可以被任何中间人缓存。若指定了 public ,则一些通常不被中间人缓存的页面,将被缓存。

    private 则表示该响应是专用于某单个用户的,中间人不能缓存此响应。

  • 缓存过期机制

    max-age=<seconds> 指令表示资源能够被缓存的最大时间,这个时间是距离请求发起的时间的秒数。一般用来缓存应用中不会改变的文件,通过手动设置一定的时长以保证缓存有效。

    1
    Cache-Control: max-age=10000
  • 缓存验证确认

    当使用了 must-revalidate 指令,那就意味着缓存在考虑使用一个资源时,必须先验证它的状态,已过期的缓存将不被使用

Pargma

Pargma 是 HTTP/1.1 标准中定义的一个 header 属性,请求中包含 Pargma 的效果跟在头信息中定义 Cache-Control: no-cache 相同,但是 HTTP 的响应头不支持这个属性,所以它不能完全替代 Cache-Control 头。

新鲜度

在过期时间之前,缓存资源是新鲜的,否则是陈旧的。一个陈旧的资源是不会被直接清除的,当客户端发起一个请求时,检索到已经有一个对应的缓存副本,则会在此次请求上附加一个 If-None-Match 头,然后再发送给服务器,以此来检查此资源是否依然是新鲜的,若返回 304 (Not Modified) ,则表示该副本是新鲜的,否则返回新的资源。

缓存验证

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有 Cache-control: must-revalidate 的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置 Advanced->Cache 为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

ETag

作为缓存的一种强校验器,ETag 响应头是一个对用户代理不透明的值。对于像浏览器这样的 HTTP UA,不知道 ETag 代表什么,不能预测它的值是多少。如果资源请求的响应头里含有 ETag, 客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。

Last-Modified 响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。

当向服务端发起缓存校验的请求时,服务端会返回 200 ok 表示返回正常的结果或者 304 Not Modified表示浏览器可以使用本地缓存文件。304 的响应头也可以同时更新缓存文档的过期时间。

需要注意的是 If-None-Match 的优先级高于 If-Modified-Since,两者同时存在的话,按照前者进行校验。

Vary 头的响应

Vary HTTP 响应头决定了对于后续的请求头,如何判断是请求一个新的资源还会使用缓存的文件。

当缓存服务器收到一个请求,只有当前的请求和原始(缓存)的请求头跟缓存的响应头里的Vary都匹配,才能使用缓存的响应。

HTTP Cookies

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

服务器通过在响应头里面添加一个 Set-Cookie 选项,来使浏览器保存下 Cookie,之后对该服务器的每一次请求中都通过 Cookie 请求头部将 Cookie 信息发送给服务器。

  • Set-Cookie 响应头部和 Cookie 请求头部

    服务器使用 Set-Cookie 响应头部向用户代理发送 Cookie 信息

    1
    Set-Cookie: <name>=<value>

    保存 Cookie 信息后,对该服务器发起的每一次新请求,浏览器都会将保存的 Cookie 信息通过 Cookeie 请求头再发送给服务器。

  • 会话期 Cookie

    会话期 Cookie 是最简单的 Cookie:浏览器关闭后它会被自动删除,即它仅在会话期内有效。

    会话期 Cookie 不需要指定过期时间或者有效期

  • 持久性 Cookie

    持久性 Cookie 指定了特定的过期时间或有效期,不会随着浏览器的关闭而被删除。

    1
    2
    Set-Cookie: id=asfwa; 
    Expires=Wed, 21 Oct 2015 07:28:00 GMT;

    设定的过期时间只和客户端有关,而不是服务端。

  • Cookie 的 SecureHttpOnly 标记

    标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务器,但由于 Cookie 固有的不安全性,敏感信息不应该通过 Cookie 传输。

    为避免跨域脚本攻击,通过 JavaScript 的 Document.cookie API 无法访问带有 HttpOnly 标记的 Cookie,它们只应发送给服务器。

  • Cookie 的作用域

    通过 DomainPath 标识可以定义 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

    Domain 指定哪些主机可以接受 Cookie,如果不指定,则默认为当前文档的主机,且不包含子域名。如果指定了,则会包含子域名。

    Path 指定主机下的哪些路径可以接受 Cookie,以字符 %x2F (即 /) 作为路径分隔符,子路径也会被匹配。

  • SameSite Cookies

    SameSite Cookie 允许服务器要求某个 Cookie 在跨站请求时不会被发送,从而可以组织跨站请求伪造攻击。

  • JavaScript 通过 document.cookies 访问 Cookie

    通过 Document.cookie 属性可创建新的 Cookie,也可以通过该属性访问非 HttpOnly 标记的 Cookie。

当机器处于不安全环境时,切记不能通过 Cookie 存储传输敏感信息。

  • 会话劫持和 XSS

    在 Web 应用中, Cookie 常用来标记用户或授权会话。因此,如果 Web 应用的 Cookie 被窃取,可能导致授权用户的会话受到攻击。

    HttpOnly 类型的 Cookie 由于阻止了 JavaScript 对其的访问性能而在一定程度上缓解了此类攻击。

  • 跨站请求伪造

    通过以下方式可以一定程度上阻止宽展请求伪造:

    • 对用户输入进行过滤来阻止 XSS
    • 任何敏感操作都需要确认
    • 用于敏感信息的 Cookie 只能拥有较短的生命周期

追踪和隐私

  • 第三方 Cookie

    每个 Cookie 都会有与之关联的域(Domain),如果 Cookie 的域和页面的域相同,那么我们称这个 Cookie 为第一方 Cookie ,如果 Cookie 的域和页面的域不同,则称之为第三方Cookie。一个页面包含图片或存放在其他域上的资源(如图片广告)时,第一方的 Cookie 也只会发送给设置它们的服务器。通过第三方组件发送的第三方 Cookie 主要用于广告和网络追踪。

  • 禁止追踪 Do-Not-Track

    虽然并没有法律或者技术手段强制要求使用DNT,但是通过DNT可以告诉Web程序不要对用户行为进行追踪或者跨站追踪。

  • 欧盟 Cookie 指令

  • 僵尸 Cookie 和删不掉的 Cookie

HTTP 访问控制

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

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(Preflight Request),从而获知服务端是否允许该跨域请求。服务端确认允许后,才发起实际的 HTTP 请求。

HTTP 消息

HTTP 消息是客户端和服务器之间交换数据的方式。它们分为两种类型:由客户端发送的用来在服务器上触发动作的消息和从服务器得到的回应。

HTTP 消息由跨越多行的用 ASCII 编码的文本信息组成。在 HTTP/1.1 和更早期的版本的协议中,消息通过连接明文发送。在 HTTP/2 中,为了优化和性能提升,人类可读的消息被分割成 HTTP 帧。

HTTP Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 请求头的名称是大小写不敏感的

// Start line
// [HTTP Method] [Request Target] [HTTP Version]
GET /img/me.png HTTP/1.1
GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1

// Headers
// Request Headers : 对请求的设置
User-Agent
Accept
Accept-Language
Accept-Encoding

// General Headers : 作用于消息整体
Connection

// Entity Headers : 作用与请求的 body 部分,如果 body 部分没有数据,则没有这部分头
Content-Type
Content-Length

// Body
// 大体上分为两类 : 单一资源 body 和多资源 body

HTTP Response

1
2
3
4
5
6
7
8
// Status line
// [HTTP Version] [Status Code] [Status Text]
HTTP/1.1 404 Not Found

// Headers
// 与 HTTP Request 结构相似

// Body

HTTP/2 帧

HTTP/1.1 的消息在性能上有着一系列的缺点:

  • Header 不像 body,它不会被压缩
  • 两个报文之间的 header 通常非常相似,但它们仍然在连接中重复传输
  • 无法复用。当在同一个服务器打开几个连接时:TCP 热连接比冷连接更加有效

所以在 HTTP/2 中,消息被分割成嵌入到流中的帧。Headers 和 Body 的帧是分开的,这使得 Headers 帧也可以被压缩。多个流可以被组合在一起,这是一种称为多路复用的技术,它使得 TCP 连接下的传输更有效率。

HTTP 会话

在类似 HTTP 的客户端-服务器协议中,会话由三个部分组成:

  • 客户端建立一个 TCP 连接
  • 客户端发送请求,等待回应
  • 服务器处理请求,做出回应

从 HTTP/1.1 起,连接在完成第三部后不再被关闭,客户端被允许发起新的请求,这意味着第二和第三部可以重复进行多次。

客户端-服务器协议中,在 HTTP 中打开一个连接,意味着在底层传输层初始化连接。使用 TCP 时,HTTP 服务器默认的端口号是 80。

HTTP 、Scoket 和 TCP 的区别

HTTP 是应用层的协议,TCP 是传输层的协议,而 Socket 是从传输层抽象的一个抽象层,本质是接口。

  1. TCP 连接与 HTTP 连接的区别

    HTTP 是基于 TCP的,客户端向服务器端发送一个 HTTP 请求时,第一步就是要建立与服务端的 TCP 连接。

  2. TCP 连接与 Socket 连接的区别

    Socket 层只是在 TCP/UDP 传输层上做的一个抽象接口层。

    基于 TCP 协议的 Socket 连接同样需要通过三次握手建立连接,是可靠的。

    基于 UDP 协议的 Socket 连接不需要建立连接的过程,不管对方能不能收到都会发送过去,是不可靠的。

  3. HTTP 连接与 Socket 连接的区别

    • HTTP 是短连接,基于 TCP 协议的 Socket 连接是长连接。尽管 HTTP 1.1 开始支持持久连接,但仍无法保证始终连接。

      而基于 TCP 协议的 Socket 连接一旦建立成功,除非一方主动断开,否则连接状态一直保持。

    • HTTP 连接,服务器无法主动发送消息,而 Socket 连接,双发请求的发送没有先后限制。

      HTTP 采用 ‘请求-响应’ 机制,在客户端没有发送请求给服务端时,服务端无法推送消息给客服端。

      Socket 连接双方类似于 P2P 的关系,可以随时互相发送消息。