目录
一. TCP协议概述
1. 概念
TCP (Transmission Control Protocol) 翻译为"传输控制协议".是一种面向连接的, 可靠传输, 面向字节流的传输层通信协议。它与 IP协议 (Internet Protocol) 共同工作, 确保确保数据能够在网络中的主机之间可靠地传输.
2. 特点
(1) 面向连接
TCP是面向连接的. 也就是说, 两台设备在通过TCP传输数据之前, 必须先建立连接; 完成数据传输之后, 要断开连接.
(TCP通过"三次握手"建立连接, 通过"四次挥手"断开连接)
(2) 可靠传输
TCP确保数据从发送方可靠地传输到接收方. 它通过序列号, 确认应答, 重传等机制来保证数据的可靠传输. (虽然有这些机制还是不能确保数据一定能够毫发无伤的传到对端, 但是TCP在尽力保障传输的可靠性). 相较而言, UDP就完全没有这种保障传输可靠性的机制.
(3) 面向字节流
TCP传输数据的基本单位是字节. 这样的传输方式更加灵活, 但是也带来了不少问题, 其中最重要的一个是"粘包问题"(下面在TCP核心机制中会讲到).
(4) 全双工通信
TCP允许数据同时在两个方向上传输, 实现全双工通信. 什么叫全双工通信? --> 就是进行通信的双方可以同时发送和接收数据 (就像打电话一样).
(5) 流量控制和拥塞控制
站在通信双方的角度: TCP通过给接收方设置接收缓冲区, 以及给发送方设置滑动窗口 来控制数据传输的速度.
站在网络连接的角度: TCP通过给发送方设置拥塞窗口 来控制发送方的数据发送速度, 以避免网络拥塞.
二. TCP协议报文格式
如上图是TCP的报文格式. 与UDP以协议相同, TCP的报文实际上也没有换行操作. 也就是说 TCP报文实际是一行文本.
1. 源端口号 和 目的端口号 (16位)
数据报发送方的端口号, 数据报接收方的端口号.
2. 序号 和 确认序号 (32位)
序号用于标识发送端发出的数据, 确认序号用于标识接收端返回的应答报文(ack).
3. 首部长度 (4位)
这里的首部就是报头的意思. 这个位置的4个bit位就存储TCP报头的长度.
我们知道, UDP的报头长度是确定的, 长度为8个字节, 由4部分组成(源端口号2字节, 目的端口号2字节, 报文长度2字节, 校验和2字节). 但是TCP的报头长度就不一样了. TCP的报头是变长的. 长度是多少就存储在这个位置(4bit).
[注]: 这里的首部长度的单位是4字节, 而不是1字节. 4bit能表示的范围是 0 - 15 , 所以说首部长度能表示的长度范围就是 0 - 60字节.
4. 保留位 (6位)
如其名, 保留位就是目前虽然不用, 但是先把这个空间申请下来. 以便将来在有需要时, 在不改变TCP报头整体结构的情况下, 增加新的功能或修改现有功能.
7. 控制位
6个位, 分别是CWR (拥塞窗口减少)、ECE(显式拥塞通知回显)、URG(紧急指针有效)、ACK(确认字段有效)、PSH(推送操作)、RST(重置连接)、SYN(同步序列编号)、FIN(结束连接)。
8. 窗口大小 (16位)
16位字段, 用于流量控制, 表示发送方发送数据的窗口的大小.
9. 校验和 (16位)
16位字段, 用于检测数据在传输过程中是否发生错误.
10. 紧急指针 (16位)
16位字段, 如果设置了URG标志, 则该字段指示紧急数据的最后一个字节的序列号.
11. 选项
选项 (options). 可变长度的字段, 用于提供更多功能. 如最大报文段长度 (MSS), 窗口缩放, 选择确认等
12. 填充
用于确保TCP头部长度为32位的整数倍。
13. 数据(正文)
正文部分是一个完整的应用层数据包. 由应用层封装好.
三. TCP协议核心机制
TCP作为一个十分强大且复杂的传输层协议, 里面涉及到的机制有很多, 我们这里只挑其中最为核心的十大机制详细讨论~
(一). 确认应答
1. 核心机制
TCP通过确认应答 (ACK) 实现可靠的数据传输.
当发送方将数据发送出去之后, 会等待接受方的确认应答(ACK). 如果收到确认应答, 说明数据已经成功到达接收方, 如果在特定等待时间内没有收到确认应答, 那么发送方就会认为数据已经丢失, 并进行重传.
[注]: 应答报文的内容一般是 "确认收到那些数据 + 下一次从哪里开始继续发"
如上图所演示, 发送方发送了一个数据(1 - 1000), 接收方如果成功接收到数据, 返回的应答报文就是 "成功接收到0-1000的数据, 下一次从1001开始发". 如果数据丢失, 那么应答报文就不会返回, 发送方在等待特定时间之后没有收到应答报文, 就会重发数据; 如果应答报文丢失, 发送方在等待特定时间之后没有收到应答报文, 也会重发数据.
总结一下, 一次数据传输, 一共会有以下三种情况:
2. 序号和确认序号
由于网络可能存在延迟, 所以可能会出现 每次所发的数据之间出现 "后发先至" 的情况. 也就是说, 后面发送的数据, 很有可能先到达接收端:
如图, 如果 (1001 - 2001) 这个数据先到达接收端, 接收端收到后返回确认应答, 确认收到 1-2000 的数据. 此时 1-1000 的数据还没有收到, 却返回了确认应答, 这就会导致数据的丢失.
为了解决上述情况, TCP中引入了"序号"和"确认序号". 每个数据有一个"序号", 其应答报文有相应的"确认序号".
例如: 某数据传输过程要传两次数据, 如果数据2先到达了接收方, 但是数据1还没到达, 那么此时数据2不会立即被接收方读取, 而是存到"接收缓冲区"里面. 等到数据1到了, 接收方才会将数据1和2一起读取. 然后返回确认应答2 (表示已经接收到1,2所有数据, 下一次从哪里开始继续发).
(二). 超时重传
1. 核心机制
前面我们提到, 发送方在发出数据后, 等待特定时间如果没有收到确认应答, 就会进行重传. 这里的特定时间不是固定的, 而是根据网络状况动态变化的.
假设某发送方发送数据后, 等待特定时间(t1)后进行第一次重传; 但是接收方在第一次重传之后还没有收到确认应答, 再次等待特定时间(t2)后, 会进行第二次重传. (t1 < t2). 每进行一次重传, 超时时间会增大, 重传的频率会降低.
如果重传的次数达到某特定值, 却还是没有收到确认应答, 就说明此时网络的丢包率已经达到了一个非常高的程度. 此时发送方就认为, 当前网络连接发生了严重故障, 不能再继续使用了. 此时发送方也就不会再尝试重传数据, 而是尝试 "复位/ 重连" (RST) : 发送一个特殊的数据包"复位报文", 如果这个时候网络恢复了, 复位报文就会重置网络连接, 使通信可以继续进行; 如果网络仍有严重问题, 复位报文也没有得到回应, 那么此时 TCP 发送方就会单方面放弃连接 (连接是通信双方分别保存对端信息, 发送方放弃连接就是发送方单方面将接收方信息删除).
2. 补充
为什么超时时间是动态的呢? --> 如果超时时间设置的太长, 会影响重传效率; 如果超时时间设置的太短, 可能会发送重复的数据包. 因此我们希望找到一个合适的超时时间, 希望如果数据没有丢失的话 确认应答最晚能在这个时间内返回. 然而, 这样的时间是随着网络情况的变化而变化的. 所以我们就需要动态调整超时时间.
Windows(Linux也相同)采用了以500ms为单位来进行超时重传控制. 每次判定的超时时间都是500ms的整数倍. 例如: 第一次重发后, 等待2*500ms, 如果还没有收到应答, 则进行第二次重传, 再等待4*500ms, 如果还没有收到应答, 则进行第三次重传 ...... 以此类推, 如果重传累积到一定次数还没有收到应答, 就进行网络重连, 如果重连失败, 则认为网络故障, 强制关闭连接(发送方单方面放弃连接).
"确认应答" 和 "超时重传", 共同构建了TCP的可靠传输机制.
(三). 连接管理
UDP 是一种无连接的通信传输协议, 因此UDP在通信时, 不检查对端是否可以通信, 直接将UDP数据包发过去. 而TCP是一种有连接的通信传输协议, 因此TCP在通信开始之前, 会做好通信双方之间的链接准备工作.
正常情况下, TCP要经过"三次握手"建立连接, 通过"四次挥手"断开连接.
1. 建立连接
建立连接需要经过 "三次握手".
(1) "三次握手"的过程
TCP在数据通信之前, 会先建立连接. 发送方通过TCP报头(报文为空) 发送一个SYN包作为建立连接的请求, 并等待对端返回确认应答. 如果接收方返回确认应答, 则认为可以进行数据通信, 建立连接成功; 如果对端没有返回确认应答, 则认为不能进行数据通信, 建立连接失败.
[注]: syn 表示 synchronized (和加锁的关键字是一个单词) ; ack 表示 acknowledge (应答).
首先我们要明确, 建立连接就是 "双方分别保存对端的信息". 如上图, 发送方A先发送建立连接的请求syn, 接收方B收到后发送确认应答ack (ack里包含B的信息), 同时接收方B再给发送方发送建立连接的请求. 发送方A接收到ack之后, A中就保存了B的信息. 然后A再给B返回一个ack (ack里包含A的信息). 这样一来, A, B 双方都包含了对端的信息, 也就表明建立连接成功.
形象一点重述: A对B说: 我想和你建立连接, 请给我你的信息 (syn). B收到syn之后, 对A说, 好的, 这是我的信息 (ack), 我也想和你建立连接 (syn). A收到ack和syn之后, A中就单方面保存了B的信息, 并且A对B说, 好的, 这是我的信息(ack). B收到ack之后, B中就保存了A的信息. 那么此时连接就建立完成了, A和B中分别保存着对端的信息.
通过上面得叙述我们可以知道, A和B双方在建立连接的过程中, 一共经历了3次交互, 我们就把这3次交互叫做 "三次握手". 这里需要注意的是, 第二次握手的 ack+syn 实际上是两次传输, 但是由于同时发生, 我们可以将其合并为一次传输.
[补充]: 从另一个角度理解三次握手: (1) A对B发送syn. B收到syn, 表明 B知道了 A的发送能力是正常的, B的接收能力是正常的. (2) B再向A发送ack+syn. A收到B的ack和syn, 表明A知道了 A的发送和接收能力是正常的, B的接收和发送能力是正常的. (3) A再向B发送ack, B收到ack, 表明B知道了 A的接收能力和发送能力是正常的, B的接受能力和发送能力是正常的. 这样一来, 通信双方就都知道了对方的发送和接收能力是正常的了.
(2) "三次握手"的原因
- 检查当前通信路径是否畅通, 如果三次握手建立连接失败, 则说明路径不畅通.
- 确认双方的接受能力和发送能力.
- 对通信过程中的关键参数进行协商 (如通信的起始序列号等).
2. 断开连接
断开连接需要经过 "四次挥手".
相较于通信双方某一端单方面把另一端的数据删除, 通过"四次挥手"断开连接是一种更加"优雅"的方式, 因为用这样的方式断开连接, 双方就都把对端的信息删掉了.
(1) "四次挥手" 的过程
[注]: fin 表示finally (结束连接), ack 表示acknowledge (应答).
与建立连接相反, 断开连接就是 "通信双方分别将对端信息删除". 如上图, 发送方A先发送断开连接的请求fin. 接收方B收到后发送确认应答ack (ack里说 B已删除A的信息). 然后接收方B再给发送方发送断开连接的请求fin. 接收方A收到后发送确认应答ack (ack里说 B删除了A的信息). 这样一来, A, B 双方都删除了对端的信息, 也就表明断开连接成功.
形象一点重述: A对B说: 我想和你断开连接, 请给删除我的信息 (fin). B收到fin之后, 对A说, 好的, 已经删除你的信息 (ack). B再对A说, 我也想和你断开连接 (fin). A收到fin之后, 对B说, 好的, 已经删除你的信息 (ack). 那么此时连接就成功断开, A和B都将对端的信息删除了.
(2) "四次挥手"的原因
主要是为了确保双方都能可靠地关闭连接,并且释放所有的网络资源.
[注]: 为什么"三次握手"中间两次传输可以合并, 而"四次挥手"中间两次传输不能合并呢?
因为"三次握手"中间的ack和syn都是操作系统内核自行调用的. 两者同时发生. 发生的时机一样, 自然就可以合并为一次传输. 但是"四次挥手"中间的ack和fin, 其中ack是由操作系统自行调用的, 而fin则是人为的通过程序调用 close(资源释放) 或者 exit(进程结束) 来触发的. 所以两个传输发生的时机不一定相同, 自然也就不能合并了.
建立连接和断开连接("三次握手"和"四次挥手") 的过程示意图如下:
(四). 滑动窗口
1. 核心机制
引入确认应答机制确实实现了可靠传输, 但是由于每次都要等到接收到上一次数据传输之后才能发送下一次数据, 这就降低了传输效率. (正所谓有所得必有所失).
那么我们如何做能把传输效率弥补回来一些, 让TCP在可靠传输的基础上也有不错的传输效率呢? --> TCP引入了 "滑动窗口" 机制. ([注]: 这里的滑动窗口机制锁提高的传输效率, 只是"亡羊补牢", 让传输效率的损失尽可能低一些, 我们不可能通过引入滑动窗口让TCP的传输效率超过UDP).
下面我们来看滑动窗口具体是怎么实现的~
如果没有滑动窗口的话, 两台设备之间的传输是这样的:
可以看到, 发送方必须收到上一次的应答报文(ack)之后, 才能继续发送数据. 这就造成了发送方有很大一部分时间是处在等待中的. 这就导致了通信效率的降低.
滑动窗口进行的操作就是 把"发一条数据等待一个应答" 改为 "发一批数据等待一批应答", 这样就把多份等待ack的时间合并成了一份.
一次批量发送的数据越多, 就可以认为效率越高. 一次批量发送的数据的量就成为 "窗口大小".
假设发送方一批发送了4条数据, 那么它就需要同时的等待4个应答. 但是我们试想, 这4个应答会同时返回吗? --> 这显然是不一定的. 如果数据1的应答报文先返回了, 此时发送方不会继续等待其他3个应答的返回, 而是再发送第5条数据, 此时等待的应答数又变成了4个. 若再有应答返回, 则在发送一个数据, 始终保证等待的应答数目是4个. 这样的话 "窗口" 就滑动起来了.
2. 丢包情况
(1) 应答报文丢失
窗口的大小比较大时, 即使某些应答报文丢失也不会造成什么影响. 如果应答报文1001丢失了, 但是应答报文2001返回了, 就相当于告诉了发送方 "1-2000 的数据都收到了, 下一次从2001开始发~" 网络通信继续往下进行. 此时虽然应答报文1001丢包了, 但是对网络通信没有任何影响.
(2) 数据丢失
刚才说应答报文丢失不会对通信造成什么影响, 也不会触发数据重传. 但是如果数据丢失了, 那此时就会触发数据重传了~
如上图, 如果发送方发送了两个数据(1-1000, 1001-2000), 收到了应答1001(1-1000的数据已收到), 但是没有收到应答2001, 说明1001-2000的数据丢包了. 此时接收方就会向发送方索要1001-2000的数据, 即使后续发送方再发来数据, 接收方也不会接收, 而是一直返回报文1001. 直到发送方收到3个重复的这样的报文之后, 发送方才意识到1001-2000的数据丢包了, 就会重发1001-2000的数据 (这个过程又叫做 "快速重传" ).
(五). 流量控制
如果发送方发送数据太快, 接收方处理不过来; 如果发送方发送数据太慢, 接收方又会空闲. 所以, 为了在这之间寻求平衡, TCP引入了 "接收缓冲区" 来进行流量控制.
[注]: 这里的 "接收缓冲区" 类似于 "阻塞队列 (Blocking Queue)".
那么, TCP是如何通过接收缓冲区实现流量控制的呢? --> 通过接收缓冲区的剩余空间大小.
如果接收缓冲区的剩余空间较大, 则可以认为接收方的处理速度较快, 此时就可以让发送方发送的快一点 (设置一个更大的窗口大小) ;
如果接收缓冲区的剩余空间较小, 则可以认为接收方的处理速度较慢, 此时就可以让发送方发送的慢一点 (设置一个更小的窗口大小).
[注]: 发送方是如何知道接收方的接收缓冲区剩余空间大小的呢? --> 在数据传输过程中, 接收方返回应答报文ack的同时, 会将接收缓冲区剩余空间大小也一并返回. 发送方收到这个ack之后就可以根据这个数据来动态调整数据发送的窗口大小了.
如果接收缓冲区满了 (剩余空间为0), 那么就会在ack报文中返回0, 代表接收缓冲区满了, 此时发送方就不会再继续发送数据了. 在等待特定时间之后, 发送方会向2接收方发送一个窗口探测包(不携带任何业务数据), 用于探测接收缓冲区是否还为满. 接收方返回一个应答报文ack, 告诉发送方当前接收缓冲区的剩余空间(是不是0). 如果不满, 则发送方接着发送数据; 如果满, 则发送方继续等待.
(六). 拥塞控制
流量控制 是站在发送方和接收方的视角来限制发送方发送数据的速度的.
拥塞控制 是站在传输链路的视角来限制发送方发送数据的速度的.
我们知道, 两台设备之间要想通信, 中间一定会经过许多交换机和路由器, 这些设备的负载也是有特定阈值的. 如果发送数据的速度太快, 超过了它们的承载范围, 那么就可能出现丢包的情况.
那么, 如何控制发送方发送数据的速度才能保证不丢包呢? --> 我们通过实验的方式确定一个合适的发送窗口大小.
如上图, 发送方发送数据的窗口大小有如下几个变化过程:
(1) 刚开始传输数据时, 拥塞窗口非常小 (先用一个很小的速度来发送数据)
因为刚开始传输数据, 网络状况未知, 所以速度很小.
(2) 发现没有丢包, 增大窗口大小 (指数增长), 短时间内达到一个很大的窗口大小.
(3) 窗口大小达到一定阈值时, 虽然还没有出现丢包的情况, 但此时不能继续指数增长, 而是变成线性增长.
因为线性增长的速度较慢一些, 不至于马上进入 "丢包状态".
(4) 窗口大小线性增长, 达到特定值之后, 就会出现丢包.
一旦出现丢包情况, 我们就需要马上减小窗口大小, 这里有两种处理方式:
[1] 经典处理方式(现已废弃): 窗口大小直接回归到慢启动时的非常小的初值, 然后再继续经历指数增长, 线性增长 ...
[2] 现在的处理方式: 窗口大小回归到比线性增长前阈值更小的一个阈值, 然后重新开始线性增长.
接下来就不断进行着线性增长 --> 回归 --> 线性增长 --> 回归 ... 的过程.
(七). 延时应答
在流量控制中, 我们对发送方窗口的大小控制时通过接收方接收缓冲区的剩余空间来动态确定的. 并且发送方是通过ack报文获取到剩余空间大小的.
我们试想, 在接收方收到数据到返回应答报文ack之间的这段时间, 接收缓冲区是不是一直在1读取数据啊? --> 是的! 那么我们在接收到数据时候不立即返回ack, 而是延迟一会, 让接收方读取一会之后再返回ack, 这样的话返回的剩余空间大小就更大了. 相应地, 发送方的发送窗口大小也就更大了.
这就是"延时应答"机制. 通过延迟一定时间, 让接收方再多读取一会数据, 从而让出更大的剩余空间.
(八). 捎带应答
两个设备在进行通信时, 往往不是一端只发送数据, 另一端只接收数据. 而是"双向通信"的, 两端都要向对端发送数据. 所以B端在向A端返回应答报文的时候, 可以连同它想发给A端的数据一起发送, 这样做的话, 两次传输就变成了一次, 从而节省了网络资源的开销.
总结一下, 捎带应答的本质就是: 数据 + 确认应答 一起发送.
典型的捎带应答的例子就是 在"三次握手"中的第二次握手, 接收方把ack和syn合并成一次进行传输.
(九). 面向字节流
TCP传输数据是面向字节流的. 什么意思? --> 例如现在要发送100个字节的数据, 可以一次发1个字节, 分100次; 也可以一次发50个字节, 分2次; 也可以一次发100个字节, 1次发完 ......
正因为TCP传输数据是面向字节流的, 所以才会出现一系列的问题, 其中最重要的是 "粘包问题".
"粘包问题", 顾名思义, 就是不同的数据之间发生了粘连, 导致数据接收方无法正常读取. 例如, 现在发送方给接收方传了3组数据: aaa, bbb, ccc. 那么在接收方缓冲区内, 数据就是aaabbbccc. 这样的数据接收方是没办法完全正确地读取的, 因为它并不知道从哪里到哪里是一个完整的数据.
解决方案:
(1) 指定分隔符: (一般适用于文本类的数据) 发送方在向接收方发送数据时, 同时也告诉接收方 数据和数据之间的分隔符是什么. 这样接收方就可以根据分隔符正确解析数据了.
(2) 指定数据的长度: (普适) 在每次发送的数据包的前几个字节(一般是2个或4个) 中存储每个数据的长度是多少. 就这样接收方就可以根据长度正确解析数据了.
(十). 异常情况处理
1. 进程崩溃
进程崩溃时, 进程中的PCB就会被回收, PCB中文件描述符表中的所有文件, 就会被系统关闭, 设备之间的网络连接, 也会正常关闭 (触发"四次挥手").
Java中常见的例子就是 程序中抛出异常, 但是没人处理, 最终异常抛到了JVM, 进程就直接结束了.
2. 主机关机
这里的关机指的是正常流程的关机 (开始 --> 菜单 --> 关机)
这样的操作同样会断开设备之间的网络连接, 触发"四次挥手".
(1) 如果四次挥手的过程很快, 四次挥手正常挥完之后, 才真正关机.
这种情况是最正常的.
(2) 如果四次挥手的过程比较慢, 还没有挥完, 主机就关机了
如上图, 这种情况B端给A端发送的fin A端就无法接收到. 当得不到A端响应时, B端会尝试重传, 当重传一定次数之后A端还是没有响应, 那么B端就会主动放弃连接 (在B中单方面删除A的信息). 这样以来最后的结果也是没有问题的
3. 主机掉电
(1) 接收方掉电
B掉电之后. A多次发送数据没有应答, 就会重传数据. 重传多次之后还是没有应答, 就会尝试"重连"(RST), "重连"也失败, A就会认为当前连接已经不能使用了, 单方面断开连接 (在A中单方面删除B的信息).
(2) 发送方掉电
发送方掉电之后, 数据就不再向B发送. 此时B在等待一段时间之后就会向A发送一个 "探测报文", 探测A是什么情况: 如果A给B返回了ack, 就代表当前连接正常, 只是A想晚点再发数据; 如果连续发了多个探测报文之后A都没有回应, 则代表A出故障了. 此时B就会单方面断开连接 (在B中单方面删除A的信息)
4. 网线断开
与主机掉电类似.
从A的视角来看: A多次发送数据没有应答, 就会重传数据. 重传多次之后还是没有应答, 就会尝试"重连", "重连"也失败, A就会认为当前连接已经不能使用了, 单方面断开连接 (在A中单方面删除B的信息).
从B的视角来看: 数据就不再向B发送. 此时B在等待一段时间之后就会向A发送一个 "探测报文", 探测A是什么情况: 如果A给B返回了ack, 就代表当前连接正常, 只是A想晚点再发数据; 如果连续发了多个探测报文之后A都没有回应, 则代表A出故障了. 此时B就会单方面断开连接 (在B中单方面删除A的信息)
四. TCP的状态
TCP (传输控制协议) 连接在其生命周期内会经历多个状态, 以下是TCP连接的主要状态:
[注]: 我们假设A端是客户端(主动建立连接/断开连接的一端), B端是服务器端(被动建立连接/断开连接的一端).
1. CLOSED
(A端和B端的状态) 初始状态, 没有建立连接.
2. LISTEN
(B端的状态) B端在等待来自A端的连接请求.
3. SYN-SENT
(A端的状态) A端发送一个SYN报文到B端, 以开始一个新的连接. 这是A端在三次握手过程中的第一个状态.
4. SYN-RECEIVED
(B端的状态) B端收到A端的SYN报文, 并回应一个ACK-SYN报文. 这是B端在三次握手过程中的第二个状态。
5. ESTABLISHED
A端收到B端的ACK-SYN报文后, 再向B端发送一个ACK报文, 此时连接建立成功.
以上 (1-5) 是建立连接阶段的状态, 下面(6-)是断开连接阶段的状态:
6. FIN-WAIT-1
(A端的状态) A端发送一个FIN报文, 等待对B端确认.
7. CLOSE-WAIT
(B端的状态) B端收到A端发来的FIN报文, 并发出确认ACK报文. 此时, 它等待应用程序关闭连接.
8. FIN-WAIT-2
(A端的状态) A端收到B端的ACK报文, 等待对方发送FIN报文.
9. LAST-ACK
(B端的状态) B端发送一个FIN报文, 等待A端的最后确认(ACK).
10. TIME-WAIT
(A端和B端的状态) A端收到B端的FIN报文并发出确认应答(ACK报文)之后, 两端都进入TIME-WAIT状态. 在等待一定时间之后, 正式断开连接.
最后, 连接最终会回到CLOSED状态 (连接完全关闭, 所有的资源都被释放).
五. TCP协议和UDP协议的区别
TCP和UDP虽然都是传输层协议, 但是两者在特性和应用场景上还是有很大区别的.
下面我们用一张图来总结两者的区别:
我们用一张更形象的图观察一下二者的区别:
类似于上图: 传输数据就像喝水, TCP有流量控制, 速度较慢, 传输可靠(水不会撒到外面); 而UDP没有流量控制, 速度较快, 传输不可靠(水可能撒到外面).
好了, 本篇文章就介绍到这里啦, 大家如果有疑问欢迎评论, 如果喜欢小编的文章, 记得点赞收藏~~