目录
[TCP和UDP的特点, 不同之处?](#TCP和UDP的特点, 不同之处?)
[三次握手 (一定是客户端主动向服务器发出握手请求)](#三次握手 (一定是客户端主动向服务器发出握手请求))
[为什么一定要三次握手, 两次不行吗](#为什么一定要三次握手, 两次不行吗)
[四次挥手 (服务器或者客户端都可以主动发出挥手请求)](#四次挥手 (服务器或者客户端都可以主动发出挥手请求))
[为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?](#为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?)
[如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?](#如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?)
[为什么要等待 2MSL?](#为什么要等待 2MSL?)
[TCP 如何保证可靠性](#TCP 如何保证可靠性)
TCP和 UDP 的特点, 不同之处?
-
TCP是面向连接的(传输数据需要建立连接), UDP是面向无连接的(传输数据不需要建立连接)
-
TCP是可靠传输, 传输的数据有序可靠不丢不重. UDP是不可靠传输, 只能是最大努力交付.
-
UDP实时性更好, 适合高速传输或者对实时性要求比较高的场景. TCP适合对可靠性要求较高的场景, 如文件传输.
-
TCP传输效率比UDP低, 因为TCP传输时需要连接,确认重传等机制保证可靠传输. UDP传输效率更高
TCP的粘包问题&&解决方案
粘包 问题:由于TCP是通过通过字节流方式传输,容易混淆包和包之间的边界,导致接收方无法去区分从哪里到哪里是一个完整的应用层数据包。

对于UDP来说,就不存在这样的问题。因为UDP是以数据包为单位读写的,一个UDP数据包承载一个应用层数据包,每次接收得到的结果就是一个完整的应用层数据包
粘包 问题的解决方案
粘包问题,在TCP的层次上,无解。
需要站在应用层解决,定义好应用层协议,明确包之间的边界
1.约定包和包之间的分隔符
2.约定包的长度

在HTTP中,两种方案都有体现
1.GET请求,没有BODY,使用空行作为结束标记
2.POST请求中,有BODY的时候,通过Content-Length决定body多长
总之,解决粘包问题,也是我们在自定义应用层协议的时候要考虑的问题
三次握手 (一定是客户端主动向服务器发出握手请求)

建立一个 TCP 连接需要"三次握手",缺一不可 :
一次握手 :客户端发送带有 SYN标志的数据包 -> 服务端,然后客户端进入 SYN_SEND状态,等待服务器的确认;
二次握手 :服务端发送带有 SYN+ACK 标志的数据包 --> 客户端,然后服务端进入 SYN_RCVD状态
三次握手 :客户端发送带有带有 ACK标志的数据包 -->服务端,然后客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手
为什么一定要三次握手, 两次不行吗
三次握手的目标是要确认客户端和服务端双方的收发能力没有问题, 都能发送接收数据. 就好比两个人交流前需要确认, 双方都不是聋子, 都不是哑巴. 如果有一方聋或者哑那就无法交流.
技巧:通过画表分析

第一次握手: 客户端发消息给服务端.
- 服务端收到消息, 此时服务端确认了自己接收能力没问题, 客户端的发送能力没问题.
第二次握手: 服务端发给客户端
-
客户端收到消息. 此时客户端能收到消息, 说明自己之前发消息一定发出去了, 说明客户端的发送能力没问题.
-
而现在收到了服务端发的数据, 那么说明服务端一定收到了数据. 故服务端的接收能力没问题.
-
那此时收到了服务端发送的数据, 说明服务端的发送没问题, 自己(客户端)的接收能没问题.
-
此时, 客户端确认了双方的收发能力都没问题.
-
这也是为什么第三次握手的时间, 客户端能够携带数据.
第三次握手: 客户端发消息给服务端. (客户端可以携带数据)
-
为什么需要第三次握手? 因为第二次握手完成后, 客户端确认了双方的收发能力都没问题. 但是服务端只知道自己能收, 对方能发, 无法确认双方的收发能力都没问题.
-
当第三次握手完毕, 服务端才能确双发的收发能力都没问题.
四次挥手 (服务器或者客户端都可以主动发出挥手请求)

为什么要四次挥手?
举个例子:A 和 B 打电话,通话即将结束后。
-
第一次挥手: A 说"我没啥要说的了"
-
第二次挥手:B 回答"我知道了",但是 B 可能还会有要说的话, A 不能要求 B 跟着自己的节奏结束通话
-
第三次挥手:于是 B 可能又巴拉巴拉说了一通,最后 B 说"我说完了"
-
第四次挥手:A 回答"知道了",这样通话才算结束
为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?
因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送
如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?
客户端没有收到 ACK 确认,会重新发送 FIN 请求。
为什么要等待 2MSL?
第四次挥手时,客户端发送给服务端的 ACK 有可能丢失,如果服务端因为某些原因而没有收到 ACK 的话,服务端就会重发 FIN,如果客户端在 2*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,保证服务端顺利关闭连接。
为什么是两倍的最长报文寿命?
因为客户端第四次挥手发送确认报文段, 该报文段到服务器端最长时间是1MSL. 若丢失, 服务端在1MSL后超时重传第三次挥手的连接释放报文段, 这个报文段到客户端的最大时间又是1MSL. 所以要等待 2MSL(一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间), 若在2MSL内没有收到超时重传的连接释放报文段, 就说明确认报文段服务端收到了, 连接正确释放了
TCP 如何保证可靠性
校验和 : TCP报文头有检验和字段, 可以用于校验报文是否损坏
序列号 与确认号: TCP 发送端发送数据包的时候会选择一个 seq序列号,接收端收到数据包后会检测数据包的完整性与顺序性, 可以根据序列号进行排序去重,如果检测通过会响应一个 ack 确认号表示收到了数据包(注意区分ACK与ack的区别)
确认应答与超时重传机制 **:**接收方收到报文后就会返回一个确认报文, 发送方发送一段时间后没收到确认报文就会进行超时重传.
- 快速重传: 接收方收到比期望序号大的报文段到达时, 就会发送一个冗余ACK, 指明下一个期待的序列号,发送端接收到3个以上的重复ACK, TCP就意识到数据发生丢失,需要重传
快速重传的体现


流量控制 **:**当接收方来不及处理发送方的数据,能通过滑动窗口,提示发送方降低发送的速率,防止包丢失
**拥塞控制:**当网络拥塞时,通过拥塞窗口,减少数据的发送,防止包丢失

TCP 流量控制
TCP 利用滑动窗口实现 流量控制 。流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据
TCP 拥塞控制
拥塞控制是依据传输链路的转发能力,进行限制的。拥塞控制和流量控制都能限制发送方的窗口大小,这两个值,
谁小谁说了算,小的那个是''拥塞窗口''。
慢启动阶段
所谓慢开始/慢启动,也就是TCP连接刚建立,一点一点地提速,试探一下网络的承受能力
慢启动步骤:
-
连接建好的开始,先初始化拥塞窗口大小为1
-
每当收到ACK确认报文, 拥塞窗口大小直接乘以2, 呈指数增长
-
还有一个ssthresh, 是一个临界值(默认为16), 当 拥塞窗口大小 >= ssthresh时,就会进入"拥塞避免阶段"
拥塞避免阶段
当拥塞窗口大小大于等于慢启动阈值ssthresh后,就进入拥塞避免阶段。
-
让拥塞窗口缓慢增大,即每经过一个往返时间 RTT(RTT 是指从发送端发送一个数据包开始,到收到接收端对该数据包的确认(ACK)为止所经历的时间) 就把发送方的拥塞窗口大小加 1
-
过了慢启动阈值后,拥塞避免阶段可以避免窗口增长过快导致窗口拥塞,可缓慢的增加调整到网络的最佳值。
快重传与快恢复
当收到3个重复ACK时,即发生丢包现象,进入快重传与快恢复阶段
-
拥塞窗口大小缩小为当前的一半
-
ssthresh设置为缩小后的拥塞窗口大小
-
然后进入拥塞避免阶段 (加法增大)
