作者:Ernest
全文较长,建议收藏
提供用户接口,特指能够发起网络流量的程序,比如QQ、web浏览器、web服务器等。数据单位为报文。
提供的是进程间的通用数据传输服务,由于应用层协议很多,定义通用的运输层协议就可以支持不断增多的应用层协议。运输层主要包括:
提供的是主机间的数据传输服务(运输层是为主机中的进程提供服务),网络层把运输层传递下来的报文段或者用户数据报封装成分组(负责选择最佳路径规划 IP 地址)。
路由器查看数据报目标 IP 地址,根据路由表为数据报选择路径。路由表中的类目可以人工添加(静态路由),也可以动态生成(动态路由)。
不同的网络类型,发送数据的机制不同,数据链路层就是将数据包封装成能够在不同的网络传输的帧,能够进行查错检验,但不纠错,检测出错误丢掉该帧。
物理层解决如何在连接各种计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体,物理层的主要任务描述为:确定与传输媒体接口的一些特性,即:
在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。
TCP/IP 模型只有四层,相当于五层协议中将数据链路层和物理层合并为网络接口层。
现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层,如下图就是 TCP/IP 体系结构的另一种表示方法:
TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占绝对重要的地位:
网络层只把分组发送到目的主机,但真正通信的并不是主机而是主机中的进程,运输层提供了进程间的逻辑通信,运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个运输层实体之间有一条端到端的逻辑通信信道。
首部字段只有8字节,包括源端口、目的端口、长度、检验和。12字节的伪首部是为了计算检验和临时添加的。
**序号 seq:**用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401 。
**确认号 ack:**期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。
数据偏移: 指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。
确认 ACK: 当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
同步 SYN: 在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
终止 FIN: 用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。
窗口: 窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。
假设 A 为客户端,B 为服务器端。
为了防止已失效的连接请求报文段突然又传送到了服务端,占用服务器资源。
现在假定一种异常情况,即 A(客户端)发出的第一个连接请求报文段并没有丢失,而是在某些网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到 B(服务端)。本来这是一个已经失效的报文段,但是 B 收到此失效的连接请求报文段后,误以为 A 又发出一次新的请求,于是就向 A 发出确认报文段,同意建立连接,假定不采用三次握手,那么只要 B 发出确认,新的连接就建立了。
由于现在 A 并没有发出建立连接的请求,因此不会理睬 B 的确认,也不会向 B 发送数据,但 B 以为新的运输连接已经建立了,并一直等待 A 发来数据,B 的许多资源就这样白费了。
采用三次握手可以防止上述现象的发生,例如在刚才的情况下,A 不会向 B 的确认发出确认,B 由于收不到确认,就知道 A 并没有要求建立连接,也就不会空转了。
数据传输结束后,通信的双方都可释放连接。现在 A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP连接。
客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态,这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。
MSL 是英文单词 Maximum Segment Lifetime 的缩写,即报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,2MSL = 2*2mins = 4mins.
客户端接收到服务器端的 FIN 报文后进入此状态,如果 B 没有收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。
等待一段时间是为了让本连接持续时间内所产生的的报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段。
当TCP两端A、B建立了连接后,如果一端拔掉网线或者拔掉电源,那么另一端能够收到通知吗?
答案是不会。
原因解析
TCP 是一种有连接的协议,这个连接并不是说有一条实际的电路,而是一种虚拟的、逻辑上的连接。TCP 建立连接和断开连接都是通过发送数据实现的,也就是我们说的三次握手、四次挥手,TCP 两端各自保存了一种数据的状态,代表这种连接,TCP 两端之间的路由设备只是将数据转发到目的地,并不知道这些数据代表什么含义,也不会在数据中心保存任何状态信息,也就是说作为中间件的路由设备根本就没有 “连接” 这个概念,只是将数据转发到目的地,只有数据的发送者和接收者两端真正知道传输的数据代表一种 逻辑上的连接 。
这就说明了一点,如果不发送数据,那么就无法断开这种逻辑连接 ,正常情况下当 TCP 的一端 A 调用了 socket
的 close 或者进程结束,操作系统就会按照 TCP 协议发送 FIN
数据报文,B 端收到后就会断开连接,但是当出现断电或者拔网线这种情况时,B 端是无法收到断开连接的请求的,所以 连接会一直保持,尽管实际上 A 已经下线了 ,至于其余的路由设备等,尽管它们知道此时 A 已经下线了,但是它们并不知道 “连接” 的存在,也就不存在所谓的通知 B 了。
解决方案
只需要让连接的双方时刻去检测这样一种状态就可以,检测的方法很简单,只要让 B 端主动通过这个连接向 A 端继续发送数据即可 ,发送数据的过程中,路由器肯定会返回给 B 一个目的不可达信息,此时 B 就知道连接已经断开了,这种保持时刻检测的方法就叫 TCP 心跳,即 KEEP_ALIVE
。
TCP协议本身就提供了一种这样的机制来探测对端的存活。TCP协议有一个KEEP_LIVE开关,只要打开这个开关就会定时发送一些数据长度为零的探测心跳包,发送的频率和次数都可以设置,具体的方法在网上搜索tcp keepalive
即可,网上有很多文章,这里不再赘述。
应用层心跳
除了使用 TCP 本身的保活开关机制,还可以在应用层主动发送心跳数据包,这二者的区别如下:
要注意的是,保持心跳一定是双向心跳,即两端都会发送试探心跳数据包,否则就失去了意义。
二者区别
窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。
接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32, 33} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。
以下进行滑动窗口模拟
在 TCP 中,滑动窗口是为了实现流量控制。如果对方发送数据过快,接收方就来不及接收,接收方就需要通告对方,减慢数据的发送。
流量控制是为了控制发送方的发送速率,宝恒接收方来得及接收,接收方发送的确认报文中窗口字段可以用来控制发送方窗口的大小,从而影响发送方的发送速率,将窗口字段设置为0,发送方不能发送数据。
拥塞控制的一般原理
如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。
为了便于讨论,做如下假设:
发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …
注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢启动阈值 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。
如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。
在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。
在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。
在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。
发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定:
查询 DNS
浏览器获得域名对应的 IP 地址后,发起 HTTP 的三次握手
TCP/IP 连接建立后,浏览器就可以向服务器发送 HTTP 请求了
TLS 握手
ClientHello
消息到服务器端,消息中同时包含了它的 TLS 版本、可用的加密算法和压缩算法ServerHello
消息,消息中包含了服务器端的 TLS 版本、服务器所选择的加密和压缩算法,以及数字认证机构(CA)签发的服务器公开证书,整数中包含公钥,客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥Finished
消息给服务器端,使用对称密钥加密这次通讯的一个散列值hash
值,然后解密客户端发来的信息,检查这两个值是否对应,如果对应,就像客户端发送一个 Finished
消息,也使用协商好的对称密钥HTTP 服务器请求处理
HTTPD(HTTP Daemon)在服务器端处理请求/响应,最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS。
HTTPD 接收请求
服务器把请求拆分为以下几个参数
HTTP 请求方法(GET
, POST
, HEAD
, PUT
, DELETE
, CONNECT
, OPTIONS
, 或者 TRACE
)。直接在地址栏中输入 URL 这种情况下,使用的是 GET 方法域名:google.com请求路径/页面:/ (我们没有请求google.com下的指定的页面,因此 / 是默认的路径)
服务器验证其上已经配置了 google.com 的虚拟主机
服务器验证 google.com 接收 GET 方法
服务器验证该用户可以使用 GET 方法(根据 IP 地址,身份信息等)
如果服务器安装了 URL 重写模块(例如 Apache 的 mod_rewrite),服务器会尝试匹配重写规则,如果匹配成功的话就重写请求
服务器根据请求信息获取相应的响应内容,这种情况下由于访问路径是 “/” ,会访问首页文件
服务器会使用指定的处理程序分析处理这个文件,并把输出结果返回给请求者
服务器接收到这个请求,根据路径参数,经过后端的一些处理生成 HTML 页面代码返回给浏览器
浏览器拿到完整的 HTML 代码开始解析渲染,如果遇到引用外部的 .js .css 等资源,同样也是一个个的 HTTP 请求,重复这个过程
浏览器根据拿到的资源对页面进行渲染,最终把一个完整的页面呈现给用户
详细版本请看:what-happens-when-zh_CN
URI 包含 URL 和 URN,目前 web 只有 URL 比较流行,见到的基本都是 URL。
服务器返回的 响应报文 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
客户端发送的 请求报文 第一行为请求行,包含了方法字段。
获取资源
当前网络请求中,绝大部分使用的是 GET 方法。
获取报文首部
和 GET 方法一样,但是不返回报文实体主体部分。
主要用于确认 URL 的有效性以及资源更新的日期时间等。
传输实体主体
POST 主要用来传输数据,而 GET 主要用来获取资源。
更多 POST 与 GET 的比较请见第八章。
上传文件
由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。
PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16
New File
对资源进行部分修改
PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。
PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 100
[description of changes]
删除文件
与 PUT 功能相反,并且同样不带验证机制。
DELETE /file.html HTTP/1.1
查询支持的方法
查询指定的 URL 能够支持的方法。
会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。
要求在与代理服务器通信时建立隧道
使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
CONNECT www.example.com:443 HTTP/1.1
追踪路径
服务器会将通信路径返回给客户端。
发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。
通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。
引申一下,从表面上看看二者的区别
HTTP协议是无状态的(stateless),指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和上一次打开这个服务器上的网页之间没有任何联系。HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)。
缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
在 HTTP/1.0 中默认使用短连接,也就是说客户端和服务器每进行一次 HTTP 操作,就建立一次链接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 WEB 页面中包含有其他资源(例如 JS 文件、图像文件、CSS 文件),每遇到这样一个 WEB 资源,浏览器就会重新建立一个 HTTP 会话。
而从 HTTP/1.1 起,默认使用长连接,在响应头加入一行代码:Connection:keep-alive
,在使用长连接的情况下,一个网页打开后客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会断,这个也就是上面说的 tcp 心跳,因为 HTTP 协议的长短连接实质上就是 TCP 协议的长短连接。
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。它用于告知服务端两个请求是否来自同一浏览器,并保持用户的登录状态。
Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。
服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
客户端之后对同一个服务器发送请求时,会从浏览器中读出 Cookie 信息通过 Cookie 请求首部字段发送给服务器。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2021 07:28:00 GMT;
通过 Document.cookie
属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨域脚本 (XSS) 攻击常常使用 JavaScript 的 Document.cookie
API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。
Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配:
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
Session 可以存储在服务器上的文件、数据库或者内存中,现在最常见的是将 Session 存储在内存型数据库中,比如 Redis。
使用 Session 维护用户登录的过程如下:
应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。
此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。
HTTP 有以下安全性问题:
HTTPs(Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。
HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说 HTTPs 使用了隧道进行通信。
通过使用 SSL,HTTPs 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。
对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。
非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。
公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。
HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输的对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。
我们知道,HTTP 协议都是明文传输内容,在早期只展示静态内容时没有问题。伴随着互联网的快速发展,人们对于网络传输安全性的要求也越来越高,HTTPS 协议因此出现。如上图所示,在 HTTPS 加密中真正起作用的其实是 SSL/TLS 协议。SSL/TLS 协议作用在 HTTP 协议之下,对于上层应用来说,原来的发送接收数据流程不变,这就很好地兼容了老的 HTTP 协议,这也是软件开发中分层实现的体现。
SSL (Secure Socket Layer,安全套接字层)
SSL为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取,当前为3.0版本。
SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
TLS (Transport Layer Security,传输层安全协议)
用于两个应用程序之间提供保密性和数据完整性。 TLS 1.0是IETF(Internet Engineering Task Force,Internet工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本,可以理解为SSL 3.1,它是写入了 RFC 的。该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。
SSL/TLS 握手是为了安全地协商出一份对称加密的秘钥,这个过程很有意思,下面我们一起来了解一下。
握手第一步是客户端向服务端发送 Client Hello 消息,这个消息里包含了一个客户端生成的随机数 Random1、客户端支持的加密套件(Support Ciphers)和 SSL Version 等信息。
第二步是服务端向客户端发送 Server Hello 消息,这个消息会从 Client Hello 传过来的 Support Ciphers 里确定一份加密套件,这个套件决定了后续加密和生成摘要时具体使用哪些算法,另外还会生成一份随机数 Random2。注意,至此客户端和服务端都拥有了两个随机数(Random1+ Random2),这两个随机数会在后续生成对称秘钥时用到。
这一步是服务端将自己的证书下发给客户端,让客户端验证自己的身份,客户端验证通过后取出证书中的公钥。
Server Hello Done 通知客户端 Server Hello 过程结束。
上面客户端根据服务器传来的公钥生成了 PreMaster Key,Client Key Exchange 就是将这个 key 传给服务端,服务端再用自己的私钥解出这个 PreMaster Key 得到客户端生成的 Random3。至此,客户端和服务端都拥有 Random1 + Random2 + Random3,两边再根据同样的算法就可以生成一份秘钥,握手结束后的应用层数据都是使用这个秘钥进行对称加密。
为什么要使用三个随机数呢?这是因为 SSL/TLS 握手过程的数据都是明文传输的,并且多个随机数种子来生成秘钥不容易被暴力破解出来。
这一步是客户端通知服务端后面再发送的消息都会使用前面协商出来的秘钥加密了,是一条事件消息。
客户端发送Finished报文。该报文包含连接至今全部报文的整理校验值。这次握手协议是否能成功,要以服务器是否能够正确解密该报文作为判定标准。
服务器同样发送Change Cipher Spec报文给客户端
服务器同样发送Finished报文给客户端
到这里,双方已安全地协商出了同一份秘钥,所有的应用层数据都会用这个秘钥加密后再通过 TCP 进行可靠传输。
最后由客户端断开连接。断开连接时,发送close_notify报文。上图做了一些省略,在这步之后再发送一种叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保护报文的完整性。
密码学中的“密码”术语与网站登录时用的密码(password)是不一样的概念,password 翻译过来其实是“口令”,它是用于认证用途的一组文本字符串。
而密码学中的密码(cipher)是一套算法(algorithm),这套算法用于对消息进行加密和解密,从明文到密文的过程称之为加密,密文反过来生成明文称之为解密,加密算法与解密算法合在一起称为密码算法。
密钥(key)是在使用密码算法过程中输入的一段参数。同一个明文在相同的密码算法和不同的密钥计算下会产生不同的密文。很多知名的密码算法都是公开的,密钥才是决定密文是否安全的重要参数,通常密钥越长,破解的难度越大,比如一个8位的密钥最多有256种情况,使用穷举法,能非常轻易的破解。根据密钥的使用方法,密码可分为对称加密和公钥加密。
对称密钥(Symmetric-key algorithm)又称为共享密钥加密,加密和解密使用相同的密钥。常见的对称加密算法有DES、3DES、AES、RC5、RC6。对称密钥的优点是计算速度快,但是它有缺点,接收者需要发送者告知密钥才能解密,因此密钥如何安全的发送给接收者成为了一个问题。
Alice 给 Bob 发送数据时,把数据用对称加密后发送给 Bob,发送过程中由于对数据进行了加密,因此即使有人窃取了数据也没法破解,因为它不知道密钥是什么。但是同样的问题是 Bob 收到数据后也一筹莫展,因为它也不知道密钥是什么,那么 Alice 是不是可以把数据和密钥一同发给 Bob 呢。当然不行,一旦把密钥和密钥一起发送的话,那就跟发送明文没什么区别了,因为一旦有人把密钥和数据同时获取了,密文就破解了。所以对称加密的密钥配是个问题。如何解决呢,公钥加密是一个办法。
公开密钥加密(public-key cryptography)简称公钥加密,这套密码算法包含配对的密钥对,分为加密密钥和解密密钥。发送者用加密密钥进行加密,接收者用解密密钥进行解密。加密密钥是公开的,任何人都可以获取,因此加密密钥又称为公钥(public key),解密密钥不能公开,只能自己使用,因此它又称为私钥(private key)。常见的公钥加密算法有 RSA。
还是以Alice 给 Bob 发送数据为例,公钥加密算法由接收者 Bob 发起
虽然公钥加密解决了密钥配送的问题,但是你没法确认公钥是不是合法的,Bob 发送的公钥你不能肯定真的是 Bob 发的,因为也有可能在 Bob 把公钥发送给 Alice 的过程中出现中间人攻击,把真实的公钥掉包替换。而对于 Alice 来说完全不知。还有一个缺点是它的运行速度比对称加密慢很多。
消息摘要(message digest)函数是一种用于判断数据完整性的算法,也称为散列函数或哈希函数,函数返回的值叫散列值,散列值又称为消息摘要或者指纹(fingerprint)。这种算法是一个不可逆的算法,因此你没法通过消息摘要反向推倒出消息是什么。所以它也称为单向散列函数。下载软件时如何确定是官方提供的完整版呢,如果有中间人在软件里面嵌入了病毒,你也不得而知。所以我们可以使用散列函数对消息进行运算,生成散列值,通常软件提供方会同时提供软件的下载地址和软件的散列值,用户把软件下载后在本地用相同的散列算法计算出散列值,与官方提供的散列值对比,如果相同,说明该软件是完成的,否则就是被人修改过了。常用的散列算法有MD5、SHA。
下载 Eclipse 时,官方网站同时提供了软件地址和消息摘要
散列函数可以保证数据的完整性,识别出数据是否被篡改,但它并不能识别出数据是不是伪装的,因为中间人可以把数据和消息摘要同时替换,数据虽然是完整的,但真实数据被掉包了,接收者收到的并不是发送者发的,而是中间人的。消息认证是解决数据真实性的办法。认证使用的技术有消息认证码和数字签名。
消息认证码(message authentication code)是一种可以确认消息完整性并进行认证(消息认证是指确认消息来自正确的发送者)的技术,简称 MAC。消息认证码可以简单理解为一种与密钥相关的单向散列函数。
Alice 给 Bob 发送消息前,先把共享密钥(key)发送给 Bob,Alice 把消息计算出 MAC 值,连同消息一起发送给 Bob,Bob 接收到消息和 MAC 值后,与本地计算得到 MAC 值对比,如果两者相同,就说明消息是完整的,而且可以确定是 Alice 发送的,没有中间人伪造。不过,消息认证码同样会遇到对称加密的密钥配送问题,因此解决密钥配送问题还是要采用公钥加密的方式。
此外,消息认证码还有一个无法解决的问题,Bob 虽然可以识别出消息的篡改和伪装,但是 Alice 可以否认说:“我没发消息,应该是 Bob 的密钥被 Attacker 盗取了,这是 Attacker 发的吧”。Alice 这么说你还真没什么可以反驳的,那么如何防止 Alice 不承认呢,数字签名可以实现。
Alice 发邮件找 Bob 借1万钱,因为邮件可以被人篡改(改成10万),也可以被伪造(Alice 根本就没发邮件,而是 Attacker 伪造 Alice 在发邮件),Alice 借了钱之后还可以不承认(不是我借的,我没有签名啊)。
消息认证码可以解决篡改和伪造的问题,Alice 不承认自己借了钱时,Bob 去找第三方机构做公正,即使这样,公正方也没法判断 Alice 有没有真的借钱,因为他们俩共享了密钥,也就是说两个都可以计算出正确的 MAC 值,Bob 说:“明明你发的消息和 MAC 值和我自己生成的 MAC 值一样,肯定是你发的消息”,Alice 说:“你把密钥透露给了其他人,是他发的邮件,你找他去吧”。Alice 矢口否认。
数字签名(Digital Signature)就可以解决否认的问题,发送消息时,Alice 和 Bob 使用不同的密钥,把公钥加密算法反过来使用,发送者 Alice 使用私钥对消息进行签名,而且只能是拥有私钥的 Alice 可以对消息签名,Bob 用配对的公钥去验证签名,第三方机构也可以用公钥验证签名,如果验证通过,说明消息一定是 Alice 发送的,抵赖也不行,因为你只有 Alice 可以生成签名。这就防止了否认的问题。
它的流程是:
第一步:发送者 Alice 把消息哈希函数处理生成消息摘要,摘要信息使用私钥加密之后生成签名,连同消息一起发送给接收者 Bob。
第二步:数据经过网络传输,Bob收到数据后,把签名和消息分别提取出来。
第三步:对签名进行验证,验证的过程是先把消息提取出来做同样的Hash处理,得到消息摘要,再与 Alice 传过来的签名用公钥解密,如果两者相等,就表示签名验证成功,否则验证失败,表示不是 Alice发的。
公钥密码在数字签名技术里面扮演举足轻重的角色,但是如何保证公钥是合法的呢,如果是遭到中间人攻击,掉包怎么办?这个时候公钥就应该交给一个第三方权威机构来管理,这个机构就是认证机构(Certification Authority)CA,CA 把用户的姓名、组织、邮箱地址等个人信息收集起来,还有此人的公钥,并由 CA 提供数字签名生成公钥证书(Public-Key Certificate)PKC,简称证书。
Alice 向 Bob 发送消息时,是通过 Bob 提供的公钥加密后的数据,而 Alice 获取的公钥并不是由 Bob 直接给的,而是由委托一个受信任的第三方机构给的。
至此,一套比较完善的数据传输方案就完成了。HTTPS(SSL/TLS)就是在这样一套流程基础之上建立起来的。
HTTP/2 通过支持请求与响应的多路复用来减少延迟,通过压缩HTTP首部字段将协议开销降至最低,同时增加对请求优先级和服务器端推送的支持。
先来理解几个概念:
帧:HTTP/2 数据通信的最小单位消息:指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成。
流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID。
HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP/1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。
**HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。**每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。
多路复用,代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP连接并发完成。 HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制,如下图,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间。
[外链图片转存中…(img-QoaxZ4qR-1646124628794)]
在 HTTP/2 中,有了二进制分帧之后,HTTP /2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2中:
这一特性,使性能有了极大提升:
服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。
服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。
HTTP 1.1请求的大小变得越来越大,有时甚至会大于TCP窗口的初始大小,因为它们需要等待带着ACK的响应回来以后才能继续被发送。HTTP/2对消息头采用HPACK(专为http/2头部设计的压缩格式)进行压缩传输,能够节省消息头占用的网络的流量。而HTTP/1.x每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。
ARP协议:
将IP地址通过广播目标MAC地址是 FF-FF-FF-FF-FF-FF 解析目标 IP 地址的 MAC 地址,扫描本网段MAC地址。
DHCP协议:
DHCP租约过程就是DHCP客户机动态获取IP地址的过程。
DHCP租约过程分为4步:
参考资料:
二进制 | 十进制 |
---|---|
1 | 1 |
10 | 2 |
100 | 4 |
1000 | 8 |
10000 | 16 |
100000 | 32 |
1000000 | 64 |
10000000 | 128 |
10000000 | 128 |
11000000 | 192 |
11100000 | 224 |
11110000 | 240 |
11111000 | 248 |
11111100 | 252 |
11111110 | 254 |
11111111 | 255 |
IP分类
公有地址:
IP分类 缺省掩码
A 1-127 /8
B 128-191 /16
C 192-223 /24
D 224-239 组播地址
E 240-247 保留地址
私有地址:
A:10.0.0.0 - 10.255.255.255
B: 172.16.0.0 - 172.31.255.255
C: 192.168.0.0 - 192.168.255.255
判断合法的主机(IP)地址:
192.168.10.240/24 合法
192.168.10.0/24 不合法,主机位全为0,网络地址
192.168.10.255/24 不合法,主机位全为1,子网广播地址
255.255.255.255 不合法,网络和主机位全为1,全网广播地址
127.x.x.x/8 不合法,本地环回地址
172.16.3.5/24 合法
192.168.5.240/32 合法
224.10.10.10.1 不合法,组播地址
300.2.4.200/24 不合法
交换机是利用物理地址或者说 MAC 地址来确定转发数据的目的地址。而路由器则是利用不同网络的 ID 号(即 IP 地址)来确定数据转发的地址。IP 地址是在软件中实现的,描述的是设备所在的网络,有时这些第三层的地址也称为协议地址或者网络地址。MAC 地址通常是硬件自带的,由网卡生产商来分配的,而且已经固化到了网卡中去,一般来说是不可更改的。而 IP 地址则通常由网络管理员或系统自动分配。
路由器和交换机的区别一:交换机是一根网线上网,但是大家上网是分别拨号,各自使用自己的宽带,大家上网没有影响。而路由器比交换机多了一个虚拟拨号功能,通过同一台路由器上网的电脑是共用一个宽带账号,大家上网要相互影响。
路由器和交换机的区别二:交换机工作在中继层,交换机根据 MAC 地址寻址。路由器工作在网络层,根据IP地址寻址,路由器可以处理 TCP/IP 协议,而交换机不可以。
路由器和交换机的区别三:交换机可以使连接它的多台电脑组成局域网,如果还有代理服务器的话还可以实现同时上网功能而且局域网所有电脑是共享它的带宽速率的,但是交换机没有路由器的自动识别数据包发送和到达地址的功能。路由器可以自动识别数据包发送和到达的地址,路由器相当于马路上的警察,负责交通疏导和指路的。
路由器和交换机的区别四:举几个例子,路由器是小邮局,就一个地址(IP),负责一个地方的收发(个人电脑,某个服务器,所以你家上网要这个东西),交换机是省里的大邮政中心,负责由一个地址给各个小地方的联系。简单的说路由器专管入网,交换机只管配送,路由路由就是给你找路让你上网的,交换机只负责开门,交换机上面要没有路由你是上不了网的。
路由器和交换机的区别五:路由器提供了防火墙的服务。路由器仅仅转发特定地址的数据包,不传送不支持路由协议的数据包传送和未知目标网络数据包的传送,从而可以防止广播风暴。
内网中192.168.1.199的前三组是网络号,后一组是主机号,子网掩码就是255.255.255.0
首先要说明的是:不是某个IP的网络号和主机号决定子网掩码是什么,而是子网掩码决定了某个IP地址的网络号与主机号是什么,IP地址是要搭配子网掩码使用的。例如上面的子网掩码决定了192.168.1.199的前三段192.168.1是网络号,最后一段199是主机号。
我们再来理解子网掩码的作用,先举个例子,市面上的两个厂家都生产电子秤,每个厂家都坚称他们的秤最准,那你是怎么知道他们的秤到底准不准?很简单,你去找一个 1KG 的国际千克原器,各放到他们的秤上测量,如果秤的测量值是1KG,那这把秤就是准的,子网掩码的作用就相当于这个大家公认的国际千克原器,是我们测量两个IP是否属于同一个网段的一个工具(应该说是让你知道某个IP地址的网络号与主机号分别是什么) 。
如果让你判断一个IP地址:192.168.1.199的网络号和主机号分别是什么?
请问你怎么判断?你凭什么说192.168.1是网络号?199是主机号?有什么根据吗?
但是如果我给你一个IP地址是以下(带子网掩码)形式的:
IP:192.168.1.199
子网掩码:255.255.255.0
那么根据大家公认的规则,你就可以得出这个IP的网络号和主机号了,怎么算呢?
子网掩码的长度和IP地址一样也是一串32位的二进制数字,只不过为人类的可读性和记忆性的方便,通常使用十进制数字来表示,例如把上面的IP地址和子网掩码都转换成相应的二进制就是下面这样的:
**十进制** **二进制**
IP 地址:192.168.1.199 ‐>11000000.10101000.00000001.11000111
子网掩码:255.255.255.0 ‐>11111111.11111111.11111111.00000000
十进制的显示形式是给人看的,二进制的显示形式是给计算机看的。。。
子网掩码的左边是网络位,用二进制数字“1”表示,1的数目等于网络位的长度;右边是主机位,用二进制数字“0”表示,0的数目等于主机位的长度。
例如上面的子网掩码255.255.255.0的 “1”的个数是左边24位,则对应IP地址左边的位数也是24位;
**十进制** **二进制**
IP 地址:192.168.1.199 ‐>11000000.10101000.00000001.11000111
子网掩码:255.255.255.0 ‐>11111111.11111111.11111111.00000000
则这个IP地址的网络号就是11000000.10101000.00000001 ,转换成十进制就是 192.168.1,网掩码255.255.255.0的 “0”的个数是右边8位,则这个IP地址的主机号就是11000111,转换成十进制就是199.