网络知识小总结
Posted guolao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络知识小总结相关的知识,希望对你有一定的参考价值。
OSI 七层
- 应用层 (HTTP、DNS、FTP)
- 表示层
- 会话层
- 传输层 (TCP、UDP)
- 网络层 (IP)
- 数据链路层 (MAC)
- 物理层
UDP
问:UDP 与 TCP 的区别是什么?
答:首先 UDP 协议是面向无连接的,也就是说不需要在正式传递数据之前先连接起双方。然后 UDP 协议只是数据报文的搬运工,不保证有序且不丢失的传递到对端,并且UDP 协议也没有任何控制流量的算法,总的来说 UDP 相较于 TCP 更加的轻便。
TCP 基本是和 UDP 反着来,建立连接断开连接都需要先需要进行握手。在传输数据的过程中,通过各种算法保证数据的可靠性,当然带来的问题就是相比 UDP 来说不那么的高效。
特点
- 无连接,即传递数据前不需要进行连接。
- 面向报文,即在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了,不进行拆分。在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不进行拼接。
- 没有拥塞控制,即允许网络发生拥堵时丢失数据。
- 支持一对多、一对一、多对一、多对多通信。
- 首部开销小,只有8个字节。
TCP
特点
- 面向连接,即传输数据前要进行连接。
- 一对一,全双工通信。
- 面向字节流。
- 提供可靠交付,有拥塞控制。
三次握手
问:为什么 TCP 建立连接需要三次握手,明明两次就可以建立起连接?
答:因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
可以想象如下场景。客户端发送了一个连接请求 A,但是因为网络原因造成了超时,这时 TCP 会启动超时重传的机制再次发送一个连接请求 B。此时请求顺利到达服务端,服务端应答完就建立了请求,然后接收数据后释放了连接。
假设这时候连接请求 A 在两端关闭后终于抵达了服务端,那么此时服务端会认为客户端又需要建立 TCP 连接,从而应答了该请求并进入 ESTABLISHED 状态。但是客户端其实是 CLOSED 的状态,那么就会导致服务端一直等待,造成资源的浪费。
- 第一次握手:客户端发送请求报文,同步位 SYN 置 1,初始序号 seq 置 x,然后进入 SYN_SEND(同步已发送) 状态,等待服务端的确认。
- 第二次握手:服务端收到请求报文,发回确认报文,SYN 和 ACK(确认位) 都置 1,确认号 ack 置 x+1,服务器自身的初始序号 seq 置 y,然后进入 SYN_RECV(同步已接收) 状态。
- 第三次握手:客服端收到确认报文,向服务端发送确认,确认位 ACK 置 1,确认号 ack 置 y+1,seq 置 x+1,然后进入 ESTABLISHED(已建立连接) 状态。服务端收到后,也进入ESTABLISHED 状态,连接完成。
四次挥手
TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。
第一次握手:客户端停止发送数据,向服务端发送一个释放连接的报文,FIN 置 1,然后进入 FIN_WAIT-1(终止等待1) 状态。
第二次握手:服务端收到连接释放报文,发回确认报文, ACK 置 1,然后进入 CLOSE-WAIT(等待关闭) 状态,客户端收到确认报文后,会进入 FIN_WAIT-2(终止等待2) 状态。
这时 TCP 连接处于半关闭状态,客户端已经停止发送数据,但服务端可能还会继续发送数据。
第三次握手:当服务端发送完数据后,会向客户端发送 连接释放报文,FIN 置 1,然后进入 LAST-ACK( 最后确认) 状态。
第四次握手:客户端收到连接释放报文后,会发回确认报文, ACK 置 1,然后进入 TIME-WAIT(时间等待) 状态,等到 2MSL(2次最长报文段寿命) 后进入 CLOSE 状态,关闭连接 。服务端收到确认,就进入 CLOSE 状态,连接正式关闭。
问:为什么客户端要进入 TIME-WAIT 状态,等待 2MSL 时间后才进入 CLOSED 状态?
答:为了保证服务端能收到客户端的确认应答。若客户端发完确认应答后直接进入 CLOSED 状态,如果确认应答因为网络问题一直没有到达,那么会造成服务端不能正常关闭。
ARQ协议-超时重传机制
停止等待协议
正常传输过程
只要 A 向 B 发送一段报文,都要停止发送并启动一个定时器,等待对端回应,在定时器时间内接收到对端应答就取消定时器并发送下一段报文。
报文丢失或出错
在报文传输的过程中可能会出现丢包。这时候超过定时器设定的时间就会再次发送丢失的数据直到对端响应,所以需要每次都备份发送的数据。
即使报文正常的传输到对端,也可能出现在传输过程中报文出错的问题。这时候对端会抛弃该报文并等待 A 端重传。
PS:一般定时器设定的时间都会大于一个 RTT 的平均时间。
ACK 超时或丢失
对端传输的应答也可能出现丢失或超时的情况。那么超过定时器时间 A 端照样会重传报文。这时候 B 端收到相同序号的报文会丢弃该报文并重传应答,直到 A 端发送下一个序号的报文。
在超时的情况下也可能出现应答很迟到达,这时 A 端会判断该序号是否已经接收过,如果接收过只需要丢弃应答即可。
从上面的描述中大家肯定可以发现这肯定不是一个高效的方式。假设在良好的网络环境中,每次发送数据都需要等待片刻肯定是不能接受的。那么既然我们不能接受这个不那么高效的协议,就来继续学习相对高效的协议吧。
连续 ARQ
在连续 ARQ 中,发送端拥有一个发送窗口,可以在没有收到应答的情况下持续发送窗口内的数据,这样相比停止等待 ARQ 协议来说减少了等待时间,提高了效率。
累计确认
连续 ARQ 中,接收端会持续不断收到报文。如果和停止等待 ARQ 中接收一个报文就发送一个应答一样,就太浪费资源了。通过累计确认,可以在收到多个报文以后统一回复一个应答报文。报文中的 ACK 标志位可以用来告诉发送端这个序号之前的数据已经全部接收到了,下次请发送这个序号后的数据。
但是累计确认也有一个弊端。在连续接收报文时,可能会遇到接收到序号 5 的报文后,并未接收到序号 6 的报文,然而序号 7 以后的报文已经接收。遇到这种情况时,ACK 只能回复 6,这样就会造成发送端重复发送数据的情况。
滑动窗口
在 TCP 中,两端其实都维护着窗口:分别为发送端窗口和接收端窗口,以字节为单位。
发送端窗口包含已发送但未收到应答的数据和可以发送但是未发送的数据。
发送端窗口是由接收窗口剩余大小决定的。接收方会把当前接收窗口的剩余大小写入应答报文,发送端收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小,所以发送窗口的大小是不断变化的。
当发送端接收到应答报文后,会随之将窗口进行滑动
滑动窗口是一个很重要的概念,它帮助 TCP 实现了流量控制的功能。接收方通过报文告知发送方还可以发送多少数据,从而保证接收方能够来得及接收数据,防止出现接收方带宽已满,但是发送方还一直发送数据的情况。
拥塞控制
拥塞处理包括了四个算法,分别为:慢开始,拥塞避免,快速重传,快速恢复。
慢开始算法
慢开始算法,顾名思义,就是在传输开始时将发送窗口慢慢指数级扩大,从而避免一开始就传输大量数据导致网络拥塞。想必大家都下载过资源,每当我们开始下载的时候都会发现下载速度是慢慢提升的,而不是一蹴而就直接拉满带宽。
慢开始算法步骤具体如下
- 连接初始设置拥塞窗口(Congestion Window) 为 1 MSS(一个分段的最大数据量)
- 每过一个 RTT 就将窗口大小乘二
- 指数级增长肯定不能没有限制的,所以有一个阈值限制,当窗口大小大于阈值时就会启动拥塞避免算法。
拥塞避免算法
拥塞避免算法相比简单点,每过一个 RTT 窗口大小只加一,这样能够避免指数级增长导致网络拥塞,慢慢将大小调整到最佳值。
在传输过程中可能定时器超时的情况,这时候 TCP 会认为网络拥塞了,会马上进行以下步骤:
- 将阈值设为当前拥塞窗口的一半
- 将拥塞窗口设为 1 MSS
- 启动拥塞避免算法
快速重传
快速重传一般和快恢复一起出现。一旦接收端收到的报文出现失序的情况,接收端只会回复最后一个顺序正确的报文序号。如果发送端收到三个重复的 ACK,无需等待定时器超时而是直接启动快速重传算法。具体算法分为两种:
TCP Taho 实现如下
- 将阈值设为当前拥塞窗口的一半
- 将拥塞窗口设为 1 MSS
- 重新开始慢开始算法
TCP Reno 实现如下
- 拥塞窗口减半
- 将阈值设为当前拥塞窗口
- 进入快恢复阶段(重发对端需要的包,一旦收到一个新的 ACK 答复就退出该阶段),这种方式在丢失多个包的情况下就不那么好了
- 使用拥塞避免算法
TCP New Ren 改进后的快恢复
TCP New Reno 算法改进了之前 TCP Reno 算法的缺陷。在之前,快恢复中只要收到一个新的 ACK 包,就会退出快恢复。
在 TCP New Reno 中,TCP 发送方先记下三个重复 ACK 的分段的最大序号。
假如我有一个分段数据是 1 ~ 10 这十个序号的报文,其中丢失了序号为 3 和 7 的报文,那么该分段的最大序号就是 10。发送端只会收到 ACK 序号为 3 的应答。这时候重发序号为 3 的报文,接收方顺利接收的话就会发送 ACK 序号为 7 的应答。这时候 TCP 知道对端是有多个包未收到,会继续发送序号为 7 的报文,接收方顺利接收并会发送 ACK 序号为 11 的应答,这时发送端认为这个分段接收端已经顺利接收,接下来会退出快恢复阶段。
HTTP
报文组成
- 请求行
- 首部
- 实体
常见状态码
2xx 成功
- 200 OK 服务端已正确处理请求
- 206 范围请求,配合 Content-Range 使用,断点续传
3xx 重定向
- 301 永久重定向,浏览器会缓存,在下次取缓存自动跳转
- 302 临时重定向,浏览器不会缓存
- 304 not modified 服务端资源未修改,缓存未过期,可使用缓存
4xx 客户端错误
- 400 客户端请求语法错误
- 401 需要认证信息
- 403 拒绝访问
- 404 请求资源未找到
5xx 服务端错误
- 500 服务端内部处理出错
- 501 服务端不支持
- 503 服务端超负载或正在停机维护
HTTPS / TLS
HTTPS 默认使用端口 443
问:TLS 与 SSL 的区别?
答:SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。
TLS:(Transport LayerSecurity,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
SSL是Netscape开发的专门用户保护Web通讯的,目前版本为3.0。最新版本的TLS 1.0是IETF(工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本,两者差别极小。
HTTPS 还是通过 HTTP 来传输信息,但是信息通过 TLS 协议进行了加密。
TLS 协议位于传输层之上,应用层之下。首次进行 TLS 协议传输需要两个 RTT ,接下来可以通过 Session Resumption 减少到一个 RTT。
在 TLS 中使用了两种加密技术,分别为:对称加密和非对称加密。
对称加密:
对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。
这种加密方式固然很好,但是问题就在于如何让双方知道秘钥。因为传输数据都是走的网络,如果将秘钥通过网络的方式传递的话,一旦秘钥被截获就没有加密的意义的。
非对称加密:
有公钥私钥之分,公钥所有人都可以知道,可以将数据用公钥加密,但是将数据解密必须使用私钥解密,私钥只有分发公钥的一方才知道。
这种加密方式就可以完美解决对称加密存在的问题。假设现在两端需要使用对称加密,那么在这之前,可以先使用非对称加密交换秘钥。
简单流程如下:首先服务端将公钥公布出去,那么客户端也就知道公钥了。接下来客户端创建一个秘钥,然后通过公钥加密并发送给服务端,服务端接收到密文以后通过私钥解密出正确的秘钥,这时候两端就都知道秘钥是什么了。
TLS 握手
- 客户端发送一个随机值以及需要的协议和加密方式。
- 服务端收到客户端的随机值,自己也产生一个随机值,并根据客户端需求的协议和加密方式来使用对应的方式,并且发送自己的证书(如果需要验证客户端证书需要说明)
- 客户端收到服务端的证书并验证是否有效,验证通过会再生成一个随机值,通过服务端证书的公钥去加密这个随机值并发送给服务端,如果服务端需要验证客户端证书的话会附带证书
- 服务端收到加密过的随机值并使用私钥解密获得第三个随机值,这时候两端都拥有了三个随机值,可以通过这三个随机值按照之前约定的加密方式生成密钥,接下来的通信就可以通过该密钥来加密解密
- 通过以上步骤可知,在 TLS 握手阶段,两端使用非对称加密的方式来通信,但是因为非对称加密损耗的性能比对称加密大,所以在正式传输数据时,两端使用对称加密的方式通信。
请求方法
问:Post 和 Get 的区别?
答:首先先引入副作用和幂等的概念。
副作用指对服务器上的资源做改变,搜索是无副作用的,注册是副作用的。
幂等指发送 M 和 N 次请求(两者不相同且都大于 1),服务器上资源的状态一致,比如注册 10 个和 11 个帐号是不幂等的,对文章进行更改 10 次和 11 次是幂等的。因为前者是多了一个账号(资源),后者只是更新同一个资源。
在规范的应用场景上说,Get 多用于无副作用,幂等的场景,例如搜索关键字。Post 多用于副作用,不幂等的场景,例如注册。
在技术上说:
- Get 请求能缓存,Post 不能
- Post 相对 Get 安全一点点,因为Get 请求都包含在 URL 里(当然你想写到
body
里也是可以的),且会被浏览器保存历史纪录。Post 不会,但是在抓包的情况下都是一样的。- URL有长度限制,会影响 Get 请求,但是这个长度限制是浏览器规定的,不是 RFC 规定的
- Post 支持更多的编码类型且不对数据类型限制
方法
- get 获取资源
- post 传输实体主体
- put 传输文件
- head 获得报文首部
- delete 删除文件
- options 询问支持的请求方法
- trace 追踪路径
- connect 要求用隧道协议连接代理
- patch 对 put 方法的补充,用来对已知资源进行局部更新
HTTP 1.0
缺陷
- 无法复用链接,请求完成就断开,需要重新慢启动和 TCP 3次握手。
- 线头阻塞(TCP 连接只能发送一个请求,前面的请求未完成前,后续的请求都在排队等待),导致请求之间互相影响。
HTTP 1.1
改进
- 长连接 (默认 keep-alive),复用连接
- 新增
- 断点续传 (206, Range, Content-Range)
- 身份认证 (401)
- 缓存
- Expires(HTTP 1.0 就已存在)
- Cache-Control
- Last-Modified
- ETag
HTTP 2.0
问:HTTP 1.xx 存在的问题?
答:
- 线头阻塞
- 多个 TCP 连接,HTTP 1.xx 版本请求并发依赖于多个TCP连接,建立TCP连接成本很高,还会存在慢启动的问题。
- 头部冗余,首部未压缩,而且每一个请求都会带上 cookie 等完全相同的首部。
- 文本格式,HTTP 1.xx 版本采用文本格式进行传输。
- 只能客户端主动请求。
- 多路复用:HTTP 2 让所有的通信都在一个 TCP 连接上完成,一个连接上面可以有任意多个流(stream),消息分割成一个或多个帧在流里面传输。
- 二进制分帧层:位于应用层和传输层之间,采用二进制协议,以采用二进制格式传输数据
- 首部压缩:需要在浏览器和服务器端双方都维护一份相同的首部名单,里面包括常见的首部,然后通过哈夫曼编码对首部进行编码,在发送请求时只需发送一个哈夫曼编码代替常见首部即可。
- 服务端推送:服务器端推送使得服务器可以预测客户端需要的资源,主动推送到客户端的缓存。
HTTP 3.0
虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,虽然这个问题并不是它本身造成的,而是底层支撑的 TCP 协议的问题。
因为 HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。
因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
QUIC
Google 搞了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上,当然 HTTP/3 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC.
虽然 UDP 效率很高,但是并不是那么的可靠。QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,比如多路复用、0-RTT、使用 TLS1.3 加密、流量控制、有序交付、重传等等功能。这里我们就挑选几个重要的功能学习下这个协议的内容。
多路复用
虽然 HTTP/2 支持了多路复用,但是 TCP 协议终究是没有这个功能的。QUIC 原生就实现了这个功能,并且传输的单个数据流可以保证有序交付且不会影响其他的数据流,这样的技术就解决了之前 TCP 存在的问题。
并且 QUIC 在移动端的表现也会比 TCP 好。因为 TCP 是基于 IP 和端口去识别连接的,这种方式在多变的移动端网络环境下是很脆弱的。但是 QUIC 是通过 ID 的方式去识别一个连接,不管你网络环境如何变化,只要 ID 不变,就能迅速重连上。
0-RTT
通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。
纠错机制
假如说这次我要发送三个包,那么协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。
当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。
当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。
缓存
强缓存
Expires
Expires的值对应一个GMT(格林尼治时间),比如Mon, 22 Jul 2002 11:12:01 GMT
来告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求,会直接从缓存中读取资源内容。
Cache-Control
优先度高于Expires
- no-cache 缓存资源,但使用缓存需要发起请求进行新鲜度验证(304)
- no-store 不缓存
- max-age 过期时间,单位为秒(s)
- public 响应可以被客户端、代理服务器缓存
- private 响应只能被客户端缓存
协商缓存
Last-Modified If-Modified-Since
服务器将资源传递给客户端时,会将资源最后更改的时间以Last-Modified: GMT
的形式加在实体首部上一起返回给客户端。
当要使用缓存时,客户端会带上If-Modified-Since: Last-Modified-value
这个请求首部去询问服务器,缓存是否新鲜,如果服务器返回 304,则直接取缓存使用。
缺陷:Last-Modified 存在一个问题,如果服务器的一个资源被修改了,但其实际内容根本没发生改变,会因为 Last-Modified 的时间匹配不上而返回整个实体给客户端(即使客户端缓存里有一模一样的资源)。
ETag If-None-Match
为了解决 Last-Modefied 存在的问题,HTTP 1.1 还推出了 ETag 首部字段,服务器会通过结合文件内容和算法(一般是hash) 来生成一个唯一值作为 ETag 的值发送给客户端。
当要使用缓存时,客户端会带上If-None-Match: ETag-value
这个请求首部去询问服务器,缓存是否新鲜,如果服务器返回 304,则直接取缓存使用。
注意:ETag 的优先级高于 Last-Modified
WebSocket
WebSocket 的目的是解决网络传输中的双向通信的问题,即实现服务端推送。
握手
请求
为了兼容性,目前 WebSocket 使用 HTTP 来完成握手,请求首部需要携带Upgrade
字段来表示服务器需要使用 WebSocket(ws、wss) 协议来进行通信。
ws 协议默认使用80
端口,wss 协议默认使用443
端口。
GET /chat HTTP/1.1 //必需。
Host: server.example.com // 必需,WebSocket服务器主机名
Upgrade: WebSocket // 必需,并且值为" WebSocket"。
Connection: Upgrade // 必需,并且值为" Upgrade"。
/* 必需,其值采用 base64 编码的随机16字节长的字符序列。 */
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
/* 浏览器必填,防止跨站攻击,浏览器一般会使用这个来标识原始域 */
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat //选填, 可用选项有子协议选择器。
Sec-WebSocket-Version: 13 //必需, 版本。
响应
服务端收到请求会切换协议进行通信,但仍会发回 HTTP 响应报文,其中会携带Sec-WebSocket-Accept
首部,表示同意连接,其值生成如下:
全局唯一(GUID,[RFC4122])标识:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
- 请求头:Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
- 取值,字符串拼接后得到:"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- SHA-1后得到: 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb20xbe 0xc4 0xea
- Base64后得到: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- 最后的结果值作为响应头 Sec-WebSocket-Accept 的值。
数据传输
WebSocket 使用数据帧(文本、二进制)进行通信,将数据分片后,包装成帧发送。
- 发送端:将消息切割成多个帧,并发送给服务端;
- 接收端:接收消息帧,并将关联的帧重新组装成完整的消息;
保持连接
WebSocket 为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。
但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。
- 发送方->接收方:ping
- 接收方->发送方:pong
以上是关于网络知识小总结的主要内容,如果未能解决你的问题,请参考以下文章