🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 前言 本文将总结后台开发中的核心网络知识。主要围绕网络层、传输层、应用层,核心为 TCP 和 HTTP 两部分。 参考资料: * 《计算机网络原理 创新教程》(韩立刚主编) * 《自顶向下计算机网络》(第4版) * 部分配图来自:[CyC2018/CS-Notes](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/CyC2018/CS-Notes) # 第一部分:传输层 ## 1\. 说一下OSI七层模型 TCP/IP四层模型 五层协议 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/assets/1536486064767.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/assets/1536486064767.png) ### (1)五层协议 * **应用层**:提供用户接口,特指能够发起网络流量的程序,比如客户端程序:QQ,MSN,浏览器等;服务器程序:web服务器,邮件服务器,流媒体服务器等等。数据单位为报文。 * **运输层**:提供的是进程间的通用数据传输服务。由于应用层协议很多,定义通用的运输层协议就可以支持不断增多的应用层协议。运输层包括两种协议: * 传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段; * 用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。 * TCP 主要提供完整性服务,UDP 主要提供及时性服务。 * **网络层**:为主机间提供数据传输服务,而运输层协议是为主机中的进程提供服务。网络层把运输层传递下来的报文段或者用户数据报封装成分组。(负责选择最佳路径 规划IP地址) * 路由器查看数据包目标IP地址,根据路由表为数据包选择路径。路由表中的类目可以人工添加(静态路由)也可以动态生成(动态路由)。 * **数据链路层**:不同的网络类型,发送数据的机制不同,数据链路层就是将数据包封装成能够在不同的网络传输的帧。能够进行差错检验,但不纠错,监测处错误丢掉该帧。 * 帧的开始和结束,透明传输,差错校验 * **物理层**:物理层解决如何在连接各种计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的主要任务描述为:确定与传输媒体的接口的一些特性,即: * 机械特性:例接口形状,大小,引线数目 * 电气特性:例规定电压范围 ( -5V 到 +5V ) * 功能特性:例规定 -5V 表示 0,+5V 表示 1 * 过程特性:也称规程特性,规定建立连接时各个相关部件的工作步骤 ### (2)ISO七层模型中表示层和会话层功能是什么? * **表示层**:数据压缩、加密以及数据描述。这使得应用程序不必担心在各台主机中表示/存储的内部格式(二进制、ASCII,比如乱码)不同的问题。 * **会话层**:建立会话,如session认证、断点续传。通信的应用程序之间建立、维护和释放面向用户的连接。通信的应用程序之间建立会话,需要传输层建立1个或多个连接。(...后台运行的木马,netstat -n) * 说明:五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。 ### (3)数据在各层之间的传递过程   在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 1. 路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。 2. 交换机只有下面两层协议 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/transfer.jpg)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/transfer.jpg) ### (4)TCP/IP四层模型 它只有四层,相当于五层协议中**数据链路层和物理层合并为网络接口层**。 现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp_ip_4.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp_ip_4.png) TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占用举足轻重的地位。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp_ip_protocol_family.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp_ip_protocol_family.png) 参考资料: * [OSI模型、TCP/IP协议栈](https://arch-long.cn/articles/network/OSI%E6%A8%A1%E5%9E%8BTCPIP%E5%8D%8F%E8%AE%AE%E6%A0%88.html) ## 2\. TCP报头格式和UDP报头格式 网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。运输层提供了进程间的逻辑通信,运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个运输层实体之间有一条端到端的逻辑通信信道。 ### (1)UDP 和 TCP 的特点 * **用户数据报协议 UDP**(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。例如:视频传输、实时通信 * **传输控制协议 TCP**(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。 ### (2)UDP 首部格式 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/udp-head2.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/udp-head2.png) 首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 ### (3)TCP 首部格式 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp-head.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp-head.png) * **序号 seq**:用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。\[301,400\]为序号301的数据长度,下一个则为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 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。 * **窗口**:窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 参考资料: * [计算机网络-运输层-笔记 | SamanthaChen's Blog](https://samanthachen.github.io/2016/08/15/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C3/) ## 3\. TCP三次握手?那四次挥手呢?如何保障可靠传输 ### (1)三次握手 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp-3.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp-3.png) **假设 A 为客户端,B 为服务器端。** * 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。 * A 向 B 发送连接请求报文段,SYN=1,ACK=0,选择一个初始的序号 seq = x。 * B 收到连接请求报文段,如果同意建立连接,则向 A 发送连接确认报文段,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 seq = y。 * A 收到 B 的连接确认报文段后,还要向 B 发出确认,确认号为 ack = y+1,序号为 seq = x+1。 * A 的 TCP 通知上层应用进程,连接已经建立。 * B 收到 A 的确认后,连接建立。 * B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程:TCP 连接已经建立。 ### (2)为什么TCP连接需要三次握手,两次不可以吗,为什么 **为了防止已失效的连接请求报文段突然又传送到了服务端,占用服务器资源。 (假设主机A为客户端,主机B为服务器端)** 现假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到B。本来这是一个已失效的报文段。但是B收到此失效的连接请求报文段后,就误认为是A有发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。 由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。 采用三次握手的办法可以防止上述现象的发生。例如在刚才的情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。 ### (3)四次挥手 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp-4.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp-4.png) 数据传输结束后,通信的双方都可释放连接。现在 A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP连接。 * A 把连接释放报文段首部的 FIN = 1,其序号 seq = u,等待 B 的确认。 * B 发出确认,确认号 ack = u+1,而这个报文段自己的序号 seq = v。(TCP 服务器进程通知高层应用进程) * 从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状态。A 不能向 B 发送数据;B 若发送数据,A 仍要接收。 * 当 B 不再需要连接时,发送连接释放请求报文段,FIN=1。 * A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(2\*2 = 4 mins)时间后释放连接。 * B 收到 A 的确认后释放连接。 ### (4)四次挥手的原因 客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 ### (5)TIME\_WAIT > MSL是Maximum Segment Lifetime英文的缩写,中文可以译为 “报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。2MSL = 2\*2mins = 4mins 客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: * 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。 * 等待一段时间是为了让本连接持续时间内所产生的所有报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段。 ### (6)如何保证可靠传输 **【详细请查阅】:《计算机网络原理 创新教程》P356——8.4节,可靠传输** * 应用数据被分割成TCP认为最适合发送的数据块。  * **超时重传**:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。 * TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。  * **校验和**:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 * TCP的接收端会丢弃重复的数据。 * **流量控制**:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的我数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。 * **拥塞控制**:当网络拥塞时,减少数据的发送。 ### (7)TCP连接状态? * CLOSED:初始状态。 * LISTEN:服务器处于监听状态。 * SYN\_SEND:客户端socket执行CONNECT连接,发送SYN包,进入此状态。 * SYN\_RECV:服务端收到SYN包并发送服务端SYN包,进入此状态。 * ESTABLISH:表示连接建立。客户端发送了最后一个ACK包后进入此状态,服务端接收到ACK包后进入此状态。 * FIN\_WAIT\_1:终止连接的一方(通常是客户机)发送了FIN报文后进入。等待对方FIN。 * CLOSE\_WAIT:(假设服务器)接收到客户机FIN包之后等待关闭的阶段。在接收到对方的FIN包之后,自然是需要立即回复ACK包的,表示已经知道断开请求。但是本方是否立即断开连接(发送FIN包)取决于是否还有数据需要发送给客户端,若有,则在发送FIN包之前均为此状态。 * FIN\_WAIT\_2:此时是半连接状态,即有一方要求关闭连接,等待另一方关闭。客户端接收到服务器的ACK包,但并没有立即接收到服务端的FIN包,进入FIN\_WAIT\_2状态。 * LAST\_ACK:服务端发动最后的FIN包,等待最后的客户端ACK响应,进入此状态。 * TIME\_WAIT:客户端收到服务端的FIN包,并立即发出ACK包做最后的确认,在此之后的2MSL时间称为TIME\_WAIT状态。 ### (8)TCP和HTTP [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/tcp-and-http.jpg)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/tcp-and-http.jpg) ## 4\. TCP连接中如果断电怎么办 TCP新手误区--心跳的意义 - CSDN博客[https://blog.csdn.net/bjrxyz/article/details/71076442](https://blog.csdn.net/bjrxyz/article/details/71076442) ## 5\. TCP和UDP区别?如何改进TCP * TCP和UDP区别 * UDP 是无连接的,即发送数据之前不需要建立连接。 * UDP 使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制。 * UDP 是面向报文的。UDP 没有拥塞控制,很适合多媒体通信的要求。 * UDP 支持一对一、一对多、多对一和多对多的交互通信。 * UDP 的首部开销小,只有 8 个字节。 * TCP 是面向连接的运输层协议。 * 每一条 TCP 连接只能有两个端点(endpoint),每一条 TCP 连接只能是点对点的(一对一)。 * TCP 提供可靠交付的服务。 * TCP 提供全双工通信。 * TCP是面向字节流。   * 首部最低20个字节。 * TCP加快传输效率的方法 * 采取一块确认的机制 ## 6\. TCP滑动窗口 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/sliding_win.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/sliding_win.png) 窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,**接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小**。 发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32, 33} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 **以下进行滑动窗口模拟** 在 TCP 中,**滑动窗口是为了实现流量控制**。如果对方发送数据过快,接收方就来不及接收,接收方就需要通告对方,减慢数据的发送。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/sliding_windows.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/sliding_windows.png) * **发送方接收到了对方发来的报文 ack = 33, win = 10,知道对方收到了 33 号前的数据**,现在期望接收 \[33, 43) 号数据。发送方连续发送了 4 个报文段假设为 A, B, C, D, 分别携带 \[33, 35), \[35, 36), \[36, 38), \[38, 41) 号数据。 * 接收方接收到了报文段 A, C,但是没收到 B 和 D,也就是只收到了 \[33, 35) 和 \[36, 38) 号数据。接收方发送回对报文段 A 的确认:ack = 35, win = 10。 * 发送方收到了 ack = 35, win = 10,对方期望接收 \[35, 45) 号数据。接着发送了一个报文段 E,它携带了 \[41, 44) 号数据。 * 接收方接收到了报文段 B: \[35, 36), D:\[38, 41),接收方发送对 D 的确认:ack = 41, win = 10. (这是一个累积确认) * 发送方收到了 ack = 41, win = 10,对方期望接收 \[41, 51) 号数据。 * …… * 需要注意的是,接收方接收 tcp 报文的顺序是不确定的,并非是一定先收到 35 再收到 36,也可能是先收到 36,37,再收到 35. 参考资料: * [20-TCP 协议(滑动窗口——基础) - CSDN博客](https://blog.csdn.net/q1007729991/article/details/70142341) * [21-TCP 协议(滑动窗口——抓包分析) - CSDN博客](https://blog.csdn.net/q1007729991/article/details/70143062) * [TCP 的那些事儿(下) | | 酷 壳 - CoolShell](https://coolshell.cn/articles/11609.html) ## 7\. TCP流量控制 流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 ## 8\. TCP拥塞处理(Congestion Handling) 拥塞控制的一般原理 * 在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏——产生拥塞(congestion)。 * 出现资源拥塞的条件:对资源需求的总和 > 可用资源 * 若网络中有许多资源同时产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/congest1.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/congest1.png) TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。 发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 为了便于讨论,做如下假设: * 接收方有足够大的接收缓存,因此不会发生流量控制; * 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/congest2-3.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/congest2-3.png) ### (1)慢开始与拥塞避免   发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...   注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢启动阈值 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。   如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。 ### (2)快重传与快恢复   在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。   在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。   在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/congest3-2.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/congest3-2.png) ### (3)发送窗口的上限值   发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定: * 发送窗口的上限值 = Min {rwnd, cwnd} * 当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值。 * 当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值。 ## 9\. 如何区分流量控制和拥塞控制 * 拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。 * 拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。 * 流量控制往往指在给定的发送端和接收端之间的点对点通信量的控制。 * 流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。 * 流量控制属于通信双方协商;拥塞控制涉及通信链路全局。 * 流量控制需要通信双方各维护一个发送窗、一个接收窗,对任意一方,接收窗大小由自身决定,发送窗大小由接收方响应的TCP报文段中窗口值确定;拥塞控制的拥塞窗口大小变化由试探性发送一定数据量数据探查网络状况后而自适应调整。 * 实际最终发送窗口 = min{流控发送窗口,拥塞窗口}。 ## 10\. 解释RTO,RTT和超时重传 * **超时重传**:发送端发送报文后若长时间未收到确认的报文则需要重发该报文。可能有以下几种情况: * 发送的数据没能到达接收端,所以对方没有响应。 * 接收端接收到数据,但是ACK报文在返回过程中丢失。 * 接收端拒绝或丢弃数据。 * **RTO**:从上一次发送数据,因为长期没有收到ACK响应,到下一次重发之间的时间。就是重传间隔。 * 通常每次重传RTO是前一次重传间隔的两倍,计量单位通常是RTT。例:1RTT,2RTT,4RTT,8RTT...... * 重传次数到达上限之后停止重传。 * **RTT**:数据从发送到接收到对方响应之间的时间间隔,即数据报在网络中一个往返用时。大小不稳定。 ## 11\. 停止等待和超时重传 ## 12\. 从输入网址到获得页面的网络请求过程 * 查询 DNS * 浏览器搜索自身的DNS缓存 * 搜索操作系统的DNS缓存,本地host文件查询 * 如果 DNS 服务器和我们的主机在同一个子网内,系统会按照下面的 ARP 过程对 DNS 服务器进行 ARP查询 * 如果 DNS 服务器和我们的主机在不同的子网,系统会按照下面的 ARP 过程对默认网关进行查询 * 浏览器获得域名对应的IP地址后,发起HTTP三次握手 * TCP/IP连接建立起来后,浏览器就可以向服务器发送HTTP请求了 * TLS 握手 * 客户端发送一个`ClientHello`消息到服务器端,消息中同时包含了它的 Transport Layer Security (TLS) 版本,可用的加密算法和压缩算法。 * 服务器端向客户端返回一个`ServerHello`消息,消息中包含了服务器端的TLS版本,服务器所选择的加密和压缩算法,以及数字证书认证机构(Certificate Authority,缩写 CA)签发的服务器公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥 * 客户端根据自己的信任CA列表,验证服务器端的证书是否可信。如果认为可信,客户端会生成一串伪随机数,使用服务器的公钥加密它。这串随机数会被用于生成新的对称密钥 * 服务器端使用自己的私钥解密上面提到的随机数,然后使用这串随机数生成自己的对称主密钥 * 客户端发送一个`Finished`消息给服务器端,使用对称密钥加密这次通讯的一个散列值 * 服务器端生成自己的 hash 值,然后解密客户端发送来的信息,检查这两个值是否对应。如果对应,就向客户端发送一个`Finished`消息,也使用协商好的对称密钥加密 * 从现在开始,接下来整个 TLS 会话都使用对称秘钥进行加密,传输应用层(HTTP)内容 * 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 和 IIS 的 URL Rewrite),服务器会尝试匹配重写规则,如果匹配上的话,服务器会按照规则重写这个请求 * 服务器根据请求信息获取相应的响应内容,这种情况下由于访问路径是 "/" ,会访问首页文件(你可以重写这个规则,但是这个是最常用的)。 * 服务器会使用指定的处理程序分析处理这个文件,假如 Google 使用 PHP,服务器会使用 PHP 解析 index 文件,并捕获输出,把 PHP 的输出结果返回给请求者 * 服务器接受到这个请求,根据路径参数,经过后端的一些处理生成HTML页面代码返回给浏览器 * 浏览器拿到完整的HTML页面代码开始解析和渲染,如果遇到引用的外部[js](http://lib.csdn.net/base/javascript),CSS,图片等静态资源,它们同样也是一个个的HTTP请求,都需要经过上面的步骤 * 浏览器根据拿到的资源对页面进行渲染,最终把一个完整的页面呈现给用户 超详细版本请转向阅读:[what-happens-when-zh\_CN](https://github.com/skyline75489/what-happens-when-zh_CN) # 第二部分:应用层(HTTP) ## 1\. URL、URI、URN区别 * URI(Uniform Resource Identifier,统一资源标识符) web服务器资源的名字,例如: index.html * URL(Uniform Resource Locator,统一资源定位符) * URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。 URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/url_uri_urn.jpg)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/url_uri_urn.jpg) ## 2\. HTTP的请求和响应报文 ### (1)请求报文 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/HTTP_RequestMessageExample.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/HTTP_RequestMessageExample.png) **GET请求** [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http_request_get.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http_request_get.png) **POST请求** [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http_request_post.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http_request_post.png) ### (2)响应报文 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/HTTP_ResponseMessageExample.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/HTTP_ResponseMessageExample.png) **200响应** [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http_response_200.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http_response_200.png) **404响应** [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http_response_400.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http_response_400.png) 参考资料: * [这一次,让我们再深入一点 - HTTP报文 - 掘金](https://juejin.im/post/5a4f782c5188257326469d7c) ## 3\. HTTP状态 服务器返回的**响应报文**中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 | 状态码 | 类别 | 原因短语 | | --- | --- | --- | | 1XX | Informational(信息性状态码) | 接收的请求正在处理 | | 2XX | Success(成功状态码) | 请求正常处理完毕 | | 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | | 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | | 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | ### (1)1XX 信息 * **100 Continue**:表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 ### (2)2XX 成功 * **200 OK** * **204 No Content**:请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 * **206 Partial Content**:表示客户端进行了范围请求。响应报文包含由 Content-Range 指定范围的实体内容。 ### (3)3XX 重定向 * **301 Moved Permanently**:永久性重定向 * **302 Found**:临时性重定向 * **303 See Other**:和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 * 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 * **304 Not Modified**:如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 * **307 Temporary Redirect**:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 ### (4)4XX 客户端错误 * **400 Bad Request**:请求报文中存在语法错误。 * **401 Unauthorized**:该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 * **403 Forbidden**:请求被拒绝,服务器端没有必要给出拒绝的详细理由。 * **404 Not Found** ### (5)5XX 服务器错误 * **500 Internal Server Error**:服务器正在执行请求时发生错误。 * **503 Service Unavailable**:服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 ## 4\. HTTP方法 客户端发送的**请求报文**第一行为请求行,包含了方法字段。 ### (1)GET > 获取资源 当前网络请求中,绝大部分使用的是 GET 方法。 ### (2)HEAD > 获取报文首部 和 GET 方法一样,但是不返回报文实体主体部分。 主要用于确认 URL 的有效性以及资源更新的日期时间等。 ### (3)POST > 传输实体主体 POST 主要用来传输数据,而 GET 主要用来获取资源。 更多 POST 与 GET 的比较请见第八章。 ### (4)PUT > 上传文件 由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。 ~~~ PUT /new.html HTTP/1.1 Host: example.com Content-type: text/html Content-length: 16 <p>New File</p> ~~~ ### (5)PATCH > 对资源进行部分修改 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] ~~~ ### (6)DELETE > 删除文件 与 PUT 功能相反,并且同样不带验证机制。 ~~~ DELETE /file.html HTTP/1.1 ~~~ ### (7)OPTIONS > 查询支持的方法 查询指定的 URL 能够支持的方法。 会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。 ### (8)CONNECT > 要求在与代理服务器通信时建立隧道 使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 ~~~ CONNECT www.example.com:443 HTTP/1.1 ~~~ [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http_connect.jpg)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http_connect.jpg) ### (9)TRACE > 追踪路径 服务器会将通信路径返回给客户端。 发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。 通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。 ## 5\. GET和POST的区别?【阿里面经OneNote】 > 就下面的找几个点和面试官侃侃而谈即可,不可能全部都记得,想到什么讲什么吧 * GET 被强制服务器支持 * 浏览器对URL的长度有限制,所以GET请求不能代替POST请求发送大量数据 * GET请求发送数据更小 * GET请求是不安全的 * GET请求是幂等的 * 幂等的意味着对同一URL的多个请求应该返回同样的结果 * POST请求不能被缓存 * POST请求相对GET请求是「安全」的 * 这里安全的含义仅仅是指是非修改信息 * GET用于信息获取,而且是安全的和幂等的 * 所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。 * POST是用于修改服务器上的资源的请求 * 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠 **引申:说完原理性的问题,我们从表面上来看看GET和POST的区别:** * GET是从服务器上获取数据,POST是向服务器传送数据。 GET和 POST只是一种传递数据的方式,GET也可以把数据传到服务器,他们的本质都是发送请求和接收结果。只是组织格式和数据量上面有差别,http协议里面有介绍 * GET是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。POST是通过HTTP POST机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。 因为GET设计成传输小数据,而且最好是不修改服务器的数据,所以浏览器一般都在地址栏里面可以看到,但POST一般都用来传递大数据,或比较隐私的数据,所以在地址栏看不到,能不能看到不是协议规定,是浏览器规定的。 * 对于GET方式,服务器端用Request.QueryString获取变量的值,对于POST方式,服务器端用Request.Form获取提交的数据。 没明白,怎么获得变量和你的服务器有关,和GET或POST无关,服务器都对这些请求做了封装 * GET传送的数据量较小,不能大于2KB。POST传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。 POST基本没有限制,我想大家都上传过文件,都是用POST方式的。只不过要修改form里面的那个type参数 * GET安全性非常低,POST安全性较高。 如果没有加密,他们安全级别都是一样的,随便一个监听器都可以把所有的数据监听到。 ## 6\. 如何理解HTTP协议是无状态的 ~~~ HTTP协议是无状态的(stateless),指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和上一次打开这个服务器上的网页之间没有任何联系。HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)。 缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 ~~~ ## 7\. 什么是短连接和长连接 在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。 而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码: ~~~js Connection:keep-alive ~~~ 在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。 HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。 ## ★ 微信二维码登录如何实现 ## 8\. Cookie HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。它用于告知服务端两个请求是否来自同一浏览器,并保持用户的登录状态。 ### (1)用途 * 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) * 个性化设置(如用户自定义设置、主题等) * 浏览器行为跟踪(如跟踪分析用户行为等) Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。 ### (2)创建过程 服务器发送的响应报文包含 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 ~~~ ### (3)分类 * 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 * 持久性 Cookie:指定一个特定的过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 ~~~ Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; ~~~ ###(4)JavaScript 获取 Cookie 通过`Document.cookie`属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。 ~~~ document.cookie = "yummy_cookie=choco"; document.cookie = "tasty_cookie=strawberry"; console.log(document.cookie); ~~~ ### (5)Secure 和 HttpOnly 标记为 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 ~~~ ### (6)作用域 Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。 Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配: * /docs * /docs/Web/ * /docs/Web/HTTP ## 9\. Session 除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 Session 可以存储在服务器上的文件、数据库或者内存中,现在最常见的是将 Session 存储在内存型数据库中,比如 Redis。 使用 Session 维护用户登录的过程如下: * 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; * 服务器验证该用户名和密码; * 如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 ID 称为 Session ID; * 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; * 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之后的业务操作。 应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/session_mechanism.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/session_mechanism.png) ## 10\. 浏览器禁用 Cookie 此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。 ## 11\. Cookie 与 Session 选择 * Cookie 只能存储 ASCII 码字符串,而 Session 则可以存取任何类型的数据,因此在考虑数据复杂性时首选 Session; * Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密; * 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。 ## 12\. HTTPs安全性 **HTTP 有以下安全性问题:** * 使用明文进行通信,内容可能会被窃听; * 不验证通信方的身份,通信方的身份有可能遭遇伪装; * 无法证明报文的完整性,报文有可能遭篡改。 HTTPs(Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。 HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说 HTTPs 使用了隧道进行通信。 通过使用 SSL,HTTPs 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 [![](https://github.com/CyC2018/Interview-Notebook/raw/master/pics/ssl-offloading.jpg)](https://github.com/CyC2018/Interview-Notebook/raw/master/pics/ssl-offloading.jpg) ### (1)对称密钥加密 对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。 * 优点:运算速度快; * 缺点:无法安全地将密钥传输给通信方。 [![](https://raw.githubusercontent.com/CyC2018/Interview-Notebook/master/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png)](https://raw.githubusercontent.com/CyC2018/Interview-Notebook/master/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png) ### (2)非对称密钥加密 非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。 公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。 * 优点:可以更安全地将公开密钥传输给通信发送方; * 缺点:运算速度慢。 [![](https://raw.githubusercontent.com/CyC2018/Interview-Notebook/master/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png)](https://raw.githubusercontent.com/CyC2018/Interview-Notebook/master/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png) ### (3)HTTPs 采用的加密方式 HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/How-HTTPS-Works2.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/How-HTTPS-Works2.png) ## 13\. SSL/TLS协议的握手过程 我们知道,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 握手是为了**安全**地协商出一份**对称加密**的秘钥,这个过程很有意思,下面我们一起来了解一下。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/https_com.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/https_com.png) ### (1)client hello 握手第一步是客户端向服务端发送 Client Hello 消息,这个消息里包含了一个客户端生成的随机数**Random1**、客户端支持的**加密套件**(Support Ciphers)和 SSL Version 等信息。 ### (2)server hello 第二步是服务端向客户端发送 Server Hello 消息,这个消息会从 Client Hello 传过来的 Support Ciphers 里确定一份加密套件,这个套件决定了后续加密和生成摘要时具体使用哪些算法,另外还会生成一份随机数**Random2**。注意,至此客户端和服务端都拥有了两个随机数(Random1+ Random2),这两个随机数会在后续生成对称秘钥时用到。 ### (3)server certificate 这一步是服务端将自己的证书下发给客户端,让客户端验证自己的身份,客户端验证通过后取出证书中的公钥。 ### (4)Server Hello Done Server Hello Done 通知客户端 Server Hello 过程结束。 ### (5)Client Key Exchange 上面客户端根据服务器传来的公钥生成了**PreMaster Key**,Client Key Exchange 就是将这个 key 传给服务端,服务端再用自己的私钥解出这个**PreMaster Key**得到客户端生成的**Random3**。至此,客户端和服务端都拥有**Random1**+**Random2**+**Random3**,两边再根据同样的算法就可以生成一份秘钥,握手结束后的应用层数据都是使用这个秘钥进行对称加密。 为什么要使用三个随机数呢?这是因为 SSL/TLS 握手过程的数据都是明文传输的,并且多个随机数种子来生成秘钥不容易被暴力破解出来。 ### (6)Change Cipher Spec(Client) 这一步是客户端通知服务端后面再发送的消息都会使用前面协商出来的秘钥加密了,是一条事件消息。 ### (7)Finished(Client) 客户端发送Finished报文。该报文包含连接至今全部报文的整理校验值。这次握手协议是否能成功,要以服务器是否能够正确解密该报文作为判定标准。 ### (8)Change Cipher Spec(Server) 服务器同样发送Change Cipher Spec报文给客户端 ### (9)Finished(Server) 服务器同样发送Finished报文给客户端 ### (10-11)Application Data 到这里,双方已安全地协商出了同一份秘钥,所有的应用层数据都会用这个秘钥加密后再通过 TCP 进行可靠传输。 ### (12)Alert:warning, close notify 最后由客户端断开连接。断开连接时,发送close\_notify报文。上图做了一些省略,在这步之后再发送一种叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保护报文的完整性。 ### (13)demand client certificate Certificate Request 是服务端要求客户端上报证书,这一步是可选的,对于安全性要求高的场景会用到。 ### check server certificate 客户端收到服务端传来的证书后,先从 CA 验证该证书的合法性,验证通过后取出证书中的服务端公钥,再生成一个随机数**Random3**,再用服务端公钥非对称加密**Random3**生成**PreMaster Key**。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/SSL_handshake.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/SSL_handshake.png) 参考资料: * [SSL\_handshake\_with\_two\_way\_authentication\_with\_certificates.svg](https://upload.wikimedia.org/wikipedia/commons/a/ae/SSL_handshake_with_two_way_authentication_with_certificates.svg) * [SSL/TLS 握手过程详解 - 简书](https://www.jianshu.com/p/7158568e4867) * [图解SSL/TLS协议 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html) * [【慕课视频】ios开发网络协议https请求视频教程](https://www.imooc.com/learn/969) * [学习HTTP/2 | levy](http://levy.work/2017-12-28-http2/) * [详解 https 是如何确保安全的? - 后端 - 掘金](https://juejin.im/entry/570469035bbb500051d4eceb) ## 14\. 数字签名、数字证书、SSL、https是什么关系? HTTPS 是建立在密码学基础之上的一种安全通信协议,严格来说是基于 HTTP 协议和 SSL/TLS 的组合。理解 HTTPS 之前有必要弄清楚一些密码学的相关基础概念,比如:明文、密文、密码、密钥、对称加密、非对称加密、信息摘要、数字签名、数字证书。接下来我会逐个解释这些术语,文章里面提到的『数据』、『消息』都是同一个概念,表示用户之间通信的内容载体,此外文章中提到了以下几个角色: * Alice:消息发送者 * Bob:消息接收者 * Attacker:中间攻击者 * Trent:第三方认证机构 ### 密码 密码学中的“密码”术语与网站登录时用的密码(password)是不一样的概念,password 翻译过来其实是“口令”,它是用于认证用途的一组文本字符串。 而密码学中的密码(cipher)是一套算法(algorithm),这套算法用于对消息进行加密和解密,从明文到密文的过程称之为加密,密文反过来生成明文称之为解密,加密算法与解密算法合在一起称为密码算法。 ### 密钥 密钥(key)是在使用密码算法过程中输入的一段参数。同一个明文在相同的密码算法和不同的密钥计算下会产生不同的密文。很多知名的密码算法都是公开的,密钥才是决定密文是否安全的重要参数,通常密钥越长,破解的难度越大,比如一个8位的密钥最多有256种情况,使用穷举法,能非常轻易的破解。根据密钥的使用方法,密码可分为对称加密和公钥加密。 ### 对称加密 对称密钥(Symmetric-key algorithm)又称为共享密钥加密,加密和解密使用相同的密钥。常见的对称加密算法有DES、3DES、AES、RC5、RC6。对称密钥的优点是计算速度快,但是它有缺点,接收者需要发送者告知密钥才能解密,因此密钥如何安全的发送给接收者成为了一个问题。 [![](https://camo.githubusercontent.com/79601fe75b0b92a8037594c5ef7b5d66f40ddcf8/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d39356632356331326165343036646132326537633462343230356134393166625f722e6a7067)](https://camo.githubusercontent.com/79601fe75b0b92a8037594c5ef7b5d66f40ddcf8/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d39356632356331326165343036646132326537633462343230356134393166625f722e6a7067) Alice 给 Bob 发送数据时,把数据用对称加密后发送给 Bob,发送过程中由于对数据进行了加密,因此即使有人窃取了数据也没法破解,因为它不知道密钥是什么。但是同样的问题是 Bob 收到数据后也一筹莫展,因为它也不知道密钥是什么,那么 Alice 是不是可以把数据和密钥一同发给 Bob 呢。当然不行,一旦把密钥和密钥一起发送的话,那就跟发送明文没什么区别了,因为一旦有人把密钥和数据同时获取了,密文就破解了。所以对称加密的密钥配是个问题。如何解决呢,公钥加密是一个办法。 ### 公钥加密(非对称加密) 公开密钥加密(public-key cryptography)简称公钥加密,这套密码算法包含配对的密钥对,分为加密密钥和解密密钥。发送者用加密密钥进行加密,接收者用解密密钥进行解密。加密密钥是公开的,任何人都可以获取,因此加密密钥又称为公钥(public key),解密密钥不能公开,只能自己使用,因此它又称为私钥(private key)。常见的公钥加密算法有 RSA。 还是以Alice 给 Bob 发送数据为例,公钥加密算法由接收者 Bob 发起 1. Bob 生成公钥和私钥对,私钥自己保存,不能透露给任何人。 2. Bob 把公钥发送给 Alice,发送过程中即使被人窃取也没关系 3. Alice 用公钥把数据进行加密,并发送给 Bob,发送过程中被人窃取了同样没关系,因为没有配对的私钥进行解密是没法破解的 4. Bob 用配对的私钥解密。 [![](https://camo.githubusercontent.com/a2e3a3a2bd133525f95303b19efc2d583c170bec/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d35626435303462383263636338376165363433613664366530393537396631335f722e6a7067)](https://camo.githubusercontent.com/a2e3a3a2bd133525f95303b19efc2d583c170bec/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d35626435303462383263636338376165363433613664366530393537396631335f722e6a7067) 虽然公钥加密解决了密钥配送的问题,但是你没法确认公钥是不是合法的,Bob 发送的公钥你不能肯定真的是 Bob 发的,因为也有可能在 Bob 把公钥发送给 Alice 的过程中出现中间人攻击,把真实的公钥掉包替换。而对于 Alice 来说完全不知。还有一个缺点是它的运行速度比对称加密慢很多。 ### 消息摘要 消息摘要(message digest)函数是一种用于判断数据完整性的算法,也称为散列函数或哈希函数,函数返回的值叫散列值,散列值又称为消息摘要或者指纹(fingerprint)。这种算法是一个不可逆的算法,因此你没法通过消息摘要反向推倒出消息是什么。所以它也称为**单向散列函数**。下载软件时如何确定是官方提供的完整版呢,如果有中间人在软件里面嵌入了病毒,你也不得而知。所以我们可以使用散列函数对消息进行运算,生成散列值,通常软件提供方会同时提供软件的下载地址和软件的散列值,用户把软件下载后在本地用相同的散列算法计算出散列值,与官方提供的散列值对比,如果相同,说明该软件是完成的,否则就是被人修改过了。常用的散列算法有MD5、SHA。 [![](https://camo.githubusercontent.com/a035b7e22ccbb171dce4b6614821b6ddc975c918/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d31643663663230356365333062303135313964386238366530333936383634335f722e6a7067)](https://camo.githubusercontent.com/a035b7e22ccbb171dce4b6614821b6ddc975c918/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d31643663663230356365333062303135313964386238366530333936383634335f722e6a7067) 下载 Eclipse 时,官方网站同时提供了软件地址和消息摘要 [![](https://camo.githubusercontent.com/87eeb1a295c364c2fdfc362b36295f6643d60ec5/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d66363838626334663166356132383838323339346232626539626232656465395f722e6a7067)](https://camo.githubusercontent.com/87eeb1a295c364c2fdfc362b36295f6643d60ec5/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d66363838626334663166356132383838323339346232626539626232656465395f722e6a7067) 散列函数可以保证数据的完整性,识别出数据是否被篡改,但它并不能识别出数据是不是伪装的,因为中间人可以把数据和消息摘要同时替换,数据虽然是完整的,但真实数据被掉包了,接收者收到的并不是发送者发的,而是中间人的。消息认证是解决数据真实性的办法。认证使用的技术有消息认证码和数字签名。 ### 消息认证码 消息认证码(message authentication code)是一种可以确认消息完整性并进行认证(消息认证是指确认消息来自正确的发送者)的技术,简称 MAC。消息认证码可以简单理解为一种与密钥相关的单向散列函数。 [![](https://camo.githubusercontent.com/8b7e1d22672d6a380872006279f56b265b0fe713/68747470733a2f2f706963312e7a68696d672e636f6d2f38302f76322d37336435326263326433383238656563346630646465356561363865666633365f722e6a7067)](https://camo.githubusercontent.com/8b7e1d22672d6a380872006279f56b265b0fe713/68747470733a2f2f706963312e7a68696d672e636f6d2f38302f76322d37336435326263326433383238656563346630646465356561363865666633365f722e6a7067) 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 可以生成签名。这就防止了否认的问题。 [![](https://camo.githubusercontent.com/472ee3fefcdca46b8e4017429ed434d67205bf7b/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d65313831396165643130393632613964313438613862343436306436303662355f722e6a7067)](https://camo.githubusercontent.com/472ee3fefcdca46b8e4017429ed434d67205bf7b/68747470733a2f2f706963332e7a68696d672e636f6d2f38302f76322d65313831396165643130393632613964313438613862343436306436303662355f722e6a7067) 它的流程是: 第一步:发送者 Alice**把消息哈希函数处理生成消息摘要,摘要信息使用私钥加密之后生成签名**,连同消息一起发送给接收者 Bob。 第二步:数据经过网络传输,Bob收到数据后,把签名和消息分别提取出来。 第三步:对签名进行验证,验证的过程是先把消息提取出来做同样的Hash处理,得到消息摘要,再与 Alice 传过来的签名用公钥解密,如果两者相等,就表示签名验证成功,否则验证失败,表示不是 Alice发的。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/ca-sign.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/ca-sign.png) ### 公钥证书 公钥密码在数字签名技术里面扮演举足轻重的角色,但是如何保证公钥是合法的呢,如果是遭到中间人攻击,掉包怎么办?这个时候公钥就应该交给一个第三方权威机构来管理,这个机构就是认证机构(Certification Authority)CA,CA 把用户的姓名、组织、邮箱地址等个人信息收集起来,还有此人的公钥,并由 CA 提供数字签名生成公钥证书(Public-Key Certificate)PKC,简称证书。 [![](https://camo.githubusercontent.com/1eaa3b5f83030666f792d0de038d40c48993bfd1/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d66393664373063306438383438393231633265626236656635393166336336365f722e6a7067)](https://camo.githubusercontent.com/1eaa3b5f83030666f792d0de038d40c48993bfd1/68747470733a2f2f706963342e7a68696d672e636f6d2f38302f76322d66393664373063306438383438393231633265626236656635393166336336365f722e6a7067) Alice 向 Bob 发送消息时,是通过 Bob 提供的公钥加密后的数据,而 Alice 获取的公钥并不是由 Bob 直接给的,而是由委托一个受信任的第三方机构给的。 1. Bob 生成密钥对,私钥自己保管,公钥交给认证机构 Trent。 2. Trent 经过一系列严格的检查确认公钥是 Bob 本人的 3. Trent 事先也生成自己的一套密钥对,用自己的私钥对 Bob 的公钥进行数字签名并生成数字证书。证书中包含了 Bob 的公钥。公钥在这里是不需要加密的,因为任何人获取 Bob 的公钥都没事,只要确定是 Bob 的公钥就行。 4. Alice 获取 Trent 提供的证书。 5. Alice 用 Trent 提供的公钥对证书进行签名验证,签名验证成功就表示证书中的公钥是 Bob 的。 6. 于是 Alice 就可以用 Bob 提供的公钥对消息加密后发送给 Bob。 7. Bob 收到密文后,用与之配对的私钥进行解密。 至此,一套比较完善的数据传输方案就完成了。HTTPS(SSL/TLS)就是在这样一套流程基础之上建立起来的。 参考资料: * [数字签名、数字证书、SSL、https是什么关系? - 知乎](https://www.zhihu.com/question/52493697) * [【Python之禅 】HTTPS 为什么更安全,先看这些](https://zhuanlan.zhihu.com/p/25324735) ## 15\. HTTP和HTTPS的区别【阿里面经OneNote】 * http是HTTP协议运行在TCP之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。 * https是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。 * https协议需要到ca申请证书,一般免费证书很少,需要交费。 * http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议 * http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。 * http的连接很简单,是无状态的 * HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全 ## 16\. HTTP2.0特性 HTTP/2的通过支持请求与响应的多路复用来减少延迟,通过压缩HTTP首部字段将协议开销降至最低,同时增加对请求优先级和服务器端推送的支持。 ### (1)二进制分帧 先来理解几个概念: 帧:HTTP/2 数据通信的最小单位消息:指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成。 流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID。 HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。 \*\*HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。\*\*每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。 ### (2)多路复用 多路复用,代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP连接并发完成。 HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制,如下图,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/pics/http2-tcp.jpg)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/pics/http2-tcp.jpg) 在 HTTP/2 中,有了二进制分帧之后,HTTP /2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2中: * 同域名下所有通信都在单个连接上完成。 * 单个连接可以承载任意数量的双向数据流。 * 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。 这一特性,使性能有了极大提升: * **同个域名只需要占用一个 TCP 连接**,消除了因多个 TCP 连接而带来的延时和内存消耗。 * 单个连接上可以并行交错的请求和响应,之间互不干扰。 * 在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。 ### (3)服务器推送 服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。 服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST\_STREAM帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。 ### (4)头部压缩 HTTP 1.1请求的大小变得越来越大,有时甚至会大于TCP窗口的初始大小,因为它们需要等待带着ACK的响应回来以后才能继续被发送。HTTP/2对消息头采用HPACK(专为http/2头部设计的压缩格式)进行压缩传输,能够节省消息头占用的网络的流量。而HTTP/1.x每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。 参考资料: * [一文读懂 HTTP/2 特性](https://zhuanlan.zhihu.com/p/26559480) * \[【体验http1.1和http2的性能对比动画】HTTP/2: the Future of the Internet | Akamai\](HTTP/2: the Future of the Internet | Akamai) # 第三部分:网络层 ## 1\. mac和ip怎么转换 **ARP协议:** 将IP地址通过广播 目标MAC地址是FF-FF-FF-FF-FF-FF 解析目标IP地址的MAC地址 扫描本网段MAC地址。 **DHCP协议:** DHCP租约过程就是DHCP客户机动态获取IP地址的过程。 DHCP租约过程分为4步: 1. 客户机请求IP(客户机发DHCPDISCOVER广播包); 2. 服务器响应(服务器发DHCPOFFER广播包); 3. 客户机选择IP(客户机发DHCPREQUEST广播包); 4. 服务器确定租约(服务器发DHCPACK/DHCPNAK广播包)。 参考资料: * [图解DHCP的4步租约过程-大浪淘沙-51CTO博客](http://blog.51cto.com/yuanbin/109574) ## 2\. IP地址子网划分 | 二进制 | 十进制 | | --- | --- | | 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 | [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/assets/%E5%AD%90%E7%BD%91%E5%88%92%E5%88%86.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/assets/%E5%AD%90%E7%BD%91%E5%88%92%E5%88%86.png) ~~~ 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 不合法 ~~~ * IP特殊地址 * 本地环回地址:127.0.0.0 – 127.255.255.255,测试主机TCP/IP协议栈是否安装正确。 * 本地链路地址:169.254.0.0 – 169.254.255.255,自动地址无法获取时系统自动配置占位。 * 受限广播地址:255.255.255.255,发往这个地址的数据不能跨越三层设备,但本地网络内所有的主机都可以接收到数据 * 参考资料: * [4internetLayer](https://canliture.github.io/s/NetworkEngineer/04internetLayer.html) * [IP地址和子网划分 - 混沌的光与影 - 博客园](https://www.cnblogs.com/lifi/p/7353279.html) * \[ZenCloud2/13-网络管理.md at a78722799508a7ac3fc7d055ff8d2d88edd0b595 · destinyplan/ZenCloud2\](ZenCloud2/13-网络管理.md at a78722799508a7ac3fc7d055ff8d2d88edd0b595 · destinyplan/ZenCloud2 ) ## 3\. 地址解析协议ARP ## 4\. 交换机和路由器的区别 1. 路由器可以给你的局域网自动分配IP,虚拟拨号,就像一个交通警察,指挥着你的电脑该往哪走,你自己不用操心那么多了。交换机只是用来分配网络数据的。 2. 路由器在网络层,路由器根据IP地址寻址,路由器可以处理TCP/IP协议,交换机不可以。 3. 交换机在中继层,交换机根据MAC地址寻址。路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。交换机可以把很多主机连起来,这些主机对外各有各的IP。 4. 路由器提供防火墙的服务,交换机不能提供该功能。集线器、交换机都是做端口扩展的,就是扩大局域网(通常都是以太网)的接入点,也就是能让局域网可以连进来更多的电脑。路由器是用来做网间连接,也就是用来连接不同的网络。 交换机是利用**物理地址或者说MAC地址**来确定转发数据的目的地址。而路由器则是利用不同网络的ID号(即IP地址)来确定数据转发的地址。IP地址是在软件中实现的,描述的是设备所在的网络,有时这些第三层的地址也称为协议地址或者网络地址。MAC地址通常是硬件自带的,由网卡生产商来分配的,而且已经固化到了网卡中去,一般来说是不可更改的。而IP地址则通常由网络管理员或系统自动分配。 **路由器和交换机的区别一**:交换机是一根网线上网,但是大家上网是分别拨号,各自使用自己的宽带,大家上网没有影响。而路由器比交换机多了一个虚拟拨号功能,通过同一台路由器上网的电脑是共用一个宽带账号,大家上网要相互影响。**路由器和交换机的区别二**:交换机工作在中继层,交换机根据MAC地址寻址。路由器工作在网络层,根据IP地址寻址,路由器可以处理TCP/IP协议,而交换机不可以。 **路由器和交换机的区别三**:交换机可以使连接它的多台电脑组成局域网,如果还有代理服务器的话还可以实现同时上网功能而且局域网所有电脑是共享它的带宽速率的,但是交换机没有路由器的自动识别数据包发送和到达地址的功能。路由器可以自动识别数据包发送和到达的地址,路由器相当于马路上的警察,负责交通疏导和指路的。 **路由器和交换机的区别四**:举几个例子,路由器是小邮局,就一个地址(IP),负责一个地方的收发(个人电脑,某个服务器,所以你家上网要这个东西),交换机是省里的大邮政中心,负责由一个地址给各个小地方的联系。简单的说路由器专管入网,交换机只管配送,路由路由就是给你找路让你上网的,交换机只负责开门,交换机上面要没有路由你是上不了网的。 **路由器和交换机的区别五**:路由器提供了防火墙的服务。路由器仅仅转发特定地址的数据包,不传送不支持路由协议的数据包传送和未知目标网络数据包的传送,从而可以防止广播风暴。 ## 5\. 子网掩码的作用 内网中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. #附录:参考资料 [OSI 七层参考模型-极客学院(4课时,47分钟)](http://www.jikexueyuan.com/course/1400.html)