跳转至

06 TCP&UDP

UDP协议详解

UDP协议的特点就是无连接的, 减少建立和释放连接的开销. UDP尽自己最大能力交付, 能发多少发多少, 但是不保证可靠交付. 因为无连接和不保证可靠交付的特点, 所以UDP协议不需要维护一些复杂的参数, 首部只有8个字节.

UDP的数据格式 协议说明:

**16位的UDP长度: **

​ 包含了, 首部长度 + 数据长度(主要目的是为了占位, 并没有实际用处, 因为可以通过网路层的头算出这个值).

​ 不管是TCP还是UDP都可以通过网路层的首部推算出来. 网络层的总长度减去网络层首部, 再减去8就是传输层UDP数据的长度.

**16位的UDP检验和: **

​ 检验和的计算内容, 伪首部 + 首部 + 数据.

​ 伪首部: 仅在计算检验和的时候起作用, 并不会传递给网络层.

和端口相关的常用命令

netstat -an: 查看被占用的端口

netstat -anb: 查看被占用的端口, 占用端口的应用程序

netstat -aon|findstr "端口": 查看某一个端口的占用情况

telnet 主机 端口 : 参考是否可以访问主机的某个端口

TCP协议详解

TCP协议数据格式

源端口 发送端的端口.

目的端口 接收方的端口

数据偏移 代表TCP协议首部的长度. 由图可知, 数据偏移占4位, 所以取值范围(0x0101 ~ 0x1111). 乘以4就是首部长度, 首部长度范围就是 20 ~ 60. TCP协议的首部和网络层的首部长度都是20到60

关于 保留 和 标志 的注意: 有些资料说保留只有3位, 标志位有9位, 这也是正确的, 因为标志的前3位是不变的, 没有作用.

**保留(Reserved)**保留字节占6位. 目前全部都是0, 没有任何作用.

**标志(Flags)**占6位, 这6位数据都是非常有用的.

​ URG(Urgent: 当URG为1时, TCP首部里面的紧急指针才有用. 紧急指针里面存放的是长度, 如果是8, 标识TCP数据里面的前8位是紧急数据.

​ ACK(Acknowledgment): 当ACK为1时, 确认号才有意义.

​ PSH(PUsh): 用在交互式网络, 可以不做了解.

​ RST(Reset): 当RST为1时, 说明连接出了严重问题, 必须释放连接, 然后需要重新建立连接, RST=1就是强制断开连接RST并不需要得到对方的响应, 仅仅是一个通知.不会再正常走四次挥手的断开流程.

​ SYN(Synchronization): 当SYN为1 , ACK=0 时, 标识这是一个建立连接的请求. 如果对方同意建立连接则回复, SYN = 1 和 ACK = 1.

​ FIN(Finsh): 当FIN = 1 时, 表示数据发送完毕, 要求释放连接.

**序号(Sequence Number): **

​ 占4个字节. 首先, 在传输过程中, 每一个字节都会有一个编号. 在建立连接后, 序号代表, 这一次传送给对方TCP数据部分的第一个字节的编号. 作用是结合发送的数据长度告诉接收方我发送了多少字节.

简单理解序号(基于建立连接之后的含义): 数据在传输层传输的是字节流, 字节在内存中都有一个自己的编号, 而且让连续的字节编号也是连续的, 一长串字节, 需要发送到网络层肯定需要分段, 分段之后, 每一段数据都有自己的TCP首部, 首部中的序号就是数据部分的第一个字节的编号.

**确认号(Acknowledgment Number): **

​ 4个字节, 确认号代表, 期望对方下一次传送过来的TCP数据部分的第一个字节的编号. 作用是告诉发送方我接收到了多少字节.

**窗口: **

​ 16个bit位, 占2个字节.

TCP协议要点

  • 可靠传输
  • 流量控制
  • 拥塞控制
  • 连接管理
  • 建立连接
  • 释放连接

TCP——可靠传输

停止等待ARQ协议

ARQ(Automatic Repeat-reQuest), 自动重传请求.

在传输过程中, A发送完M1之后就会有一个等待时间, 在这个等待时间之内, 如果B正常收到M1并且正常返回确认收到M1, A也收到了确认消息, 这就是无差错的情况. 如果超时了, A就会选择重新发送M1.

但是可能会出现, 第一次发送M1时, B返回的第一次确认延迟了, 所以A发送了第二次M1,成功接受, 成功确认之后, 第一次的确认此时被A接受到了, 这时A收下迟到的M1, 但是不做任何反应了.

注意: 如果有一个包重传了N次还一直失败, 会一直传送直到成功吗?这取决于系统的设置, 有些系统重传5次还没成功就会发送reset报文(RST)断开TCP连接.

连续ARQ协议 + 滑动窗口协议

停止等待ARQ协议因为有等待时间并且一次发送确认流程只能传送一组数据, 效率非常低, 所以就出现了 连续ARQ协议 + 滑动窗口协议.

连续ARQ协议

将M1到M4连续发出去, 这4个是排好顺序的, 如果返回接收到M4就说明前的全部接收成功, 接着再发后面的数据, 而一次发送多少是由滑动窗口决定的.

滑动窗口协议

接受方的传输层会有一个缓存, 这个缓存大小就决定了发送方滑动窗口的大小, 滑动窗口的大小是由接收方通过接收确认返回时发送给发送方的.

image-20220828161311672

序号和确认号工作流程

TCP01_序号_确认号

图中细节1: 当发送方已经接收到接收方返回的接收确认, 就会将缓存中已经确认接收到的数据清除.

图中细节2: 当接收方第二次接收到新的数据时, 上一次已经确认接收到的数据就会在接收端的缓存中传递给上一层并清除.

图中ACK=601解释: 在第二次发送数据时, 发现第7个数据段丢失, 但是第8个数据段成功发送并接收, 这时发送确认返回时就会把将期望的ACK填成601, 此时按理应该发送的时 第7 , 8 , 9 , 10组数据, 但是实际情况是发现第8组数据已经接收成功了(前提是在接收TCP确认的TCP首部的选项部分发现了SACK选择确认协议), 所以就会忽略第8组数据, 直发7, 9, 10组数据. 那发送方是如何知道第8组数据已经发送成功的呢?答案就是SACK选择确认协议. SACK是放到TCP首部 选项 里的.

注意: 上图中的Sep序号是计算之后的虚拟包序号, 原本的Sep是一个非常大的值, 这个值需要减去一个固定值, 才能得到图中的序号.

滑动窗口大小和数据大小不一样的情况:

​ 接受方告诉发送方滑动窗口大小是400. 但是这个时候发送方只有200的数据. 正常来说, 如果发送方发送了400的数据, 接受方会给发送方一个返回确认. 但这次只接受到200的数据, 接收方等待一段时间, 如果还没有数据就直接返回接收到200数据的确认.

SACK选择确认

image-20220828171533732

只发送没有接收到的包, 避免发送已发送过的包. SACK可以精确的告诉发送端, 那些数据接收到了, 那些没有. 左边界到右边界之间的数据已经收到的.

为什么要在传输层进行数据段的拆分?

​ 答: 在网络层片偏移那里我说过 所有凡是从传输层来的数据, 网络层都不需要切片 . 原因就是传输层已经将数据段拆分好了. 那为什么不在网络层分片呢?因为只有传输层的TCP协议才能保证可靠传输, 并且在传输层分片可以提高效率. 如果我们在网络层进行切片, 中途有一包数据丢失, 丢失之后不会重传, 对方传输层无法成功组装数据, 不会返回ACK确认, 不返回ACK确认, 发送方的传输层超时之后就会将整一大段数据再次发送, 网络层再次分包, 如果没有丢失还好, 如果有丢失, 又需要重传整段数据, 效率很低. 如果在传输层进行数据分段, 中间有一段数据丢了, 只需要重传这一段数据就可以了.

TCP——流量控制

概念

如果接收方的缓存区满了, 对方还在疯狂的发送数据, 接收方就只能吧收到的数据包丢掉, 大量的丢包就会浪费网络资源. 这是就需要流量控制.

什么是流量控制:

​ 让发送方的发送速率不要太快, 让接收方来得及处理.

流量控制原理

通过确认报文中窗口字段来控制发送方的发送速率.

发送方的发送窗口大小不能超过接收方给的窗口大小.

当发送方接收到接收窗口的大小为0时, 发送放就会停止发送数据.

流量控制过程中有一种特殊情况, 当接收方给发送方发送了窗口为0的报文段, 发送方停止发送, 过了一段时间, 接收方又有了一些存储空间, 给发送方发送接收窗口非0的报文段, 但是这个报文段丢失. 发送方就一直处于停止发送的状态, 接收方也接受不到数据, 就一直陷入僵持.

如何解决这一现象呢?

​ 当发送方收到接收窗口大小为0的报文段时, 发送方停止发送并且同时开启一个定时器, 隔一段时间就发个测试报文去询问接收发最新的窗口大小. 如果接收方返回的窗口大小还是0, 则发送方再次刷新启动定时器.

注意: 流量控制只存在于发送发和接收方之间, 是点对点的, 彼此告诉对方窗口的大小, 来控制发送速率.

TCP——拥塞控制

概念:

​ 防止过多的数据注入到网路中, 避免网路中的路由或者链路过载.

拥塞控制是一个全局性的过程, 涉及到所有的主机, 路由器, 以及降低网络传输性能有关的所有因素是大家共同努力的结果.

负载和链路吞吐量之间的大致关系

image-20220909153709804

(当负载逐渐变大时, 链路吞吐量前期也会跟随变大, 如果负载达到一个特定值时, 此时吞吐量就会开始变小, 丢掉多余的包. 最终吞吐量会变成0, 进入死锁. )

拥塞控制方法

必知名词

  1. MSS(Maximum Segment Size):每个段最大的数据部分大小, 在建立连接时确认. 确认过程: 在建立连接时, 接收方和发送给的MSS是不一样的, 选择小的那个MSS作为这次连接在传输数据的过程中的数据段最大值. 比如建立连接时, 协商最小的MSS是1300个字节, 那么传输过程种数据段就不能大于1300个字节, 如果发送窗口的大小是3600个字节, 那么这一次传输一共传输3组数据比较合适.
  2. cwnd(congestion window)拥塞窗口: 是发送方根据网络自己调整大小的窗口.
  3. rwnd(receive window)接收窗口: rwnd的大小是由接收方决定的, 是接收方告诉发送方最多可以发送多少字节, 是一次发送的总数据大小, 不是发送一段数据的大小. 比如这一次发送4组数据, 每一组数据时1200字节, 那么rwnd就是4800字节.
  4. swnd(send window)发送窗口: 大小是由发送方决定的, 发送窗口真正发送数据的大小. 它的取值是 min(cwnd,rwnd).

三个窗口的关系:

​ 在发送数据时, 如果接收方最多可以接收3000字节的数据, 那么rwnd就是3000, 当发送方知道rwnd的值时, 发送方会根据情况判断:

​ 第一种情况: 网络状况良好. 此时发送方得知cwnd的大小是6000字节, 但是接收方只能接收3000字节, 发送方就会将swnd的大小的最大值设置成3000字节, 然后根据MSS和swnd的值将数据拆成个数合理的数据段发送给接收方.

​ 第二种情况: 网络状况不好. 此时发送放得知cwnd的大小是2000字节, 即使接收方可以接收3000字节, 发送方也只会把swnd的大小最大值设置成2000字节, 然后根据MSS和swnd的值将数据拆成个数合理的数据段发送给接收方.

无论什么情况, swnd的最大值只能是cwnd和rwnd的最小值.

拥塞控制的方法

  1. 慢开始

慢开始算法的思路: 当主机开始发送数据时, 如果立即把大量数据字节注入到网络中, 那么就有可能引起网络拥塞, 因为现在并不清楚网络的负荷情况. 经验证明, 较好的方法是先探测一下, 即由小到大逐渐增大发送窗口, 也就是说, 由小到大逐渐增大拥塞窗口数值. 通常在刚刚开始发送报文段时, 先把拥塞窗口cwnd设置为一个最大报文段MSS数值. 而在每收到一个对新的报文段的确认后, 把拥塞窗口增加至多一个MSS的数值, 用这样的方法逐步增大发送方的拥塞窗口cwnd, 可以使分组注入到网络的速率更加合理.

  1. image-20220909170840278

    由上图可知, MSS的值是100, rwnd的值是3000, 不考虑cwnd的情况下, 传输过程应该是每包数据段的大小是100, 一次能够发30包数据才对. 但是图中是慢开始, 将拥塞窗口设置成和MSS一样的值, 这就意味着一次之只能发一包数据, 确认收到之后可以发2包, 然后发4包...... 所以慢开始就是cwnd的初始值设置的非常小, 然后随着数据包被接收方确认(收到一个ACK), cwnd就成倍增长(指数级).

  2. 拥塞避免

image-20220909171559453

分析上图就可以知道拥塞避免是建立在慢开始的基础上的. 拥塞避免就是设置一个慢开始的阈值, 当cwnd达到阈值之后就开始降低增长速率(从指数级别降低到线性级别, 这就是 加法增大), 让cwnd增长的缓慢一点, 以防止网络过早出现拥塞. 当网络出现拥塞的时候(如果出现丢包就代表网络以及拥塞了), 立刻将慢开始的cwnd阈值降低为网络拥塞时的一半, 同时重新开始慢开始算法, 就是将慢开始的cwnd值回复成初始值, 然后重复步骤.

  1. 快重传

image-20220909172718284

所谓快重传就是字面意思, 立刻将丢失的包重新传送, 而不等计时器到期, 直接就重传. 那么它怎么判断那些包是需要快速传送的呢?如图, M3丢失了, 返回收到的确认就只有M2, 如果收到三个连续的对M2的重复确认就直接重传M3,不会有任何等待时间.

  1. 快恢复

image-20220909180019175

当发送方连续收到三个重复确认时, 说明有数据包丢失, 这就意味着网络出现了拥塞, 就执行"乘法减小"算法, 把ssthresh就按少为拥塞峰值的一半. 就是本来时重新开始慢开始, 要将拥塞窗口设置成初始值, 但是如果能够重复的收到3个确认, 说明网络拥塞的不是太严重, 没有必要将拥塞窗口设置成初始, 只需要将拥塞窗口的值设置成新的阈值(拥塞峰值的一半), 然后开始执行拥塞避免("加法增大"). 这就是所谓的快恢复.

TCP——连接管理

建立连接——序号和确认号

建立连接的流程. 在HTTP请求之前, 需要建立TCP连接, 建立连接有三次握手.

第一次握手:

​ SYN = 1 , ACK = 0 , 代表客户端去跟服务器请求建立连接, 建立连接时数据部分是没有长度的.

第二次握手:

​ SYN = 1 , ACK = 1 , 代表服务器同意建立连接, 返回连接确认.

第三次握手:

​ SYN = 0 , ACK = 1 , 客户端指向服务端.

经历了上面的三次握手, 连接就建立成功了, 这时候就需要客户端向服务器发送HTTP请求.

然后就是连续ARQ协议传输数据

image-20220912194245670

确认号序号在建立连接时的状态

在上述的TCP要点中以及大致的介绍过确认号和序号的工作流程. 这里将详细说明.

首先, 确认号在抓包软件中是ACK表示确认号, 是4个字节的数, 同时还有一个标志为的ACK,这个ACK只有一个字节, 只是是0或1, 如果为1, 确认号才有意义.

我们先来看一发送数据时的图(这是一张理想的图, 发送一个包, 就返回一个确认, 同时序号和确认号都是相对值, 不是真实值. ):

TCP04_序号确认号01_相对

由上图可知:

​ 第一次发送数据时, sep = 1 , 数据长度为 1460

​ 第一次返回确认, ACK = 1461 , 收到1460个数据

​ 第二次发送数据时, sep = 1461 , 数据长度为 1460

​ 第二次返回确认, ACK = 2921. 加上已接收的共收到 2920 个数据

​ 第三次发送数据时: sep = 2921 , 数据长度为 1460

​ 第三次返回确认: ACK = 4381.加上已接收的共收到 4380 个数据

由图可知, 已经收到的所有数据 + 1 , 就是ACK.

上述所使用的序号和确认号都是相对值, 真实的情况中, 不会使用相对值, 为什么真实情况不用相对值呢?因为相对值的序号和确认号都太简单了, 如果被攻击之后, 很容易就模拟出后面需要发送的包的序号和长度, 因此序号和确认号的真实值是一个非常大的值.

在第一次握手的时候, 客户端给服务端了一个序号初始值, 这个序号初始值是随机产生的一个很大的数.

在第二次握手的时候, 服务端也会给客户端一个序号初始值.

客户端和服务端彼此都可以作为发送方和接收方, 在发送数据时, 谁发送的数据就用谁的初始值.

分析下面的图, 结合建立连接时的图看. 紫色是客户端, 蓝色是服务端.

image-20220912202154258

在第一次握手时: 客户端向服务端发送建立连接的请求. 标志位 SYN = 1 , ACK = 0 (说明此时确认号没有作用, 所以确认号为0), 数据部分是0, 所以序号也是0, 因为这是我是建立连接的请求, 不需要发送任何数据.

在第二次握手时, 服务端已经接收到了客户端建立连接的请求, 服务端需要对这个建立连接的请求做出回应. 标志位 SYN = 1 , ACK = 1(说明确认号有意义). 此时数据长度还是0, 发送的数据只有一个tcp的头, 所以相对的序号还是0. 然后服务器希望客户端下一次发送的数据从第1个字节开始, 所以确认号ack变成1.

在第三次握手时, 客户端收到确认, 需要给服务端发送第三次握手请求. 这次客户端发送的数据部分还是0, 按理说序号应该是0才对, 但是这里比较特殊, 为了响应第2次握手, 所以这里的序号要变成第二次握手的确认号1. 至于ack为1的原因和第二次握手一样, 第二次握手接收到0个字节, 那么这次它就需要下一次服务端能够发送0字节的下一个字节开始的数据, 所以ack = 1.

紧接着客户端向服务器发送http请求, 这一次请求的确认号和序号和第三次握手是一样的, 因为这一次请求都是对第二次握手的回应. 但这一次和第三次握手相比, 它发送了k字节的数据给服务端.

image-20220912202217425

第5个步骤: 这个步骤是响应第4个http请求的步骤, http请求发送了k字节的数据, 所以服务端希望客户端下次发送k字节开始的下一个字节的数据, 所以 ack = k + 1 , 表示前k个字节我已经接收到了. http请求中ack = 1, 说明客户端希望服务端发送序号为1的包, 所以这一步骤的seq = 1. len = b1 , 表示服务端已经把序号为1, 长度为b1的数据段发送了.

第6个步骤: 因为是连续ARQ协议, 所以这次还是服务端对客户端http请求的响应, 所以ack = k + 1 , 因为第5个步骤以及发送了b1字节的数据, 这次就需要发送b1字节后面的数据, 所以seq = b1 + 1 , len = b2.

第7个步骤: 同样是对http请求的响应, 所以ack = k + 1 , 应为上两个步骤已经发送了b1 + b2 个字节的数据, 所以这次需要发送b1 + b2后一个字节的数据, 所以seq = b1 + b2 + 1 , len = b3.

第8个步骤同上.

image-20220918123725204

第9个步骤, 是连续收到服务器4个TCP数据段之后, 客户端做出的响应. 按照连续ARQ协议可知, 这一次响应需要响应最后接收到的数据段, 所以响应第8个步骤, 因为第8个步骤的总共发送了, b1 + b2 + b3 + b4个字节, 此时客户端需要告诉服务器我已经接收到了 b1 + b2 + b3 + b4个字节的数据, 并且希望服务端下次发送已接受到字节的下一个字节为序号的数据 , 所以 ack = b1 + b2 + b3 + b4 +1. 在第8个步骤中, 因为ack = k + 1 , 所以客户端需要发送k+1序号的数据给服务器, seq = k + 1 . 但是这里是一个确认接收的步骤, 没有发送任何字节给服务端, len = 0 . 所以第10个步骤的ack = k + 1. 这就造成了在抓包的时候, 出现很多ack值相同的情况.

上面的关于确认号和序号的解析, 都是在相对序号和确认号的情况. 如果需要分析原生情况, 只需要加上原生序号的初始值即可.

序号和确认号变化过程:

image-20220920160943610

建立连接——3次握手

三次握手时客户端和服务器的状态变化:

image-20220920161400852

由图可知:

​ 在没有发送第一次握手之前, 客户端处于关闭状态, 服务器处于监听状态, 服务器一直在监听某个端口, 等待客户端连接. ​ 第一次握手, SYN = 1 , ACK = 1 , seq = x (x时客户端序号初始值) , 这时客户端的SYN = 1 已经发送, 所以客户端进入同步已发送状态. 服务器接收到客户端第一次握手请求时, 知道SYN同步字段为1, 然后对第一次握手做出响应, 向客户端发送第二次握手, 接着进入同步已接收状态. 当客户端接收到SYN =1 ,ACK = 1的连接请求确认, 然后发送SYN = 1 ,ACK = 1 的第三次握手之后, 客户端进入连接已建立状态, 服务器也是, 在接收到第三次握手后, 进入连接已建立状态. 然后服务端和客户端开始数据传输.

前两次握手的特点

1.SYN都时1, 之后的SYN都是0.

2.数据部分的长度都是0.

3.TCP的头部一般情况下都是32字节, 多出的12字节放到选项里, 是一些确认信息.

4.客户端和服务端会通过前两次握手交换确认一些信息. 例如:

​ MSS: 每个段最大的数据部分大小, 客户端和服务器都会提供一个, 选择小的那个.

​ SACK: 是否选择确认.

​ Window scale(窗口缩放系数): 只有两个字节, 用两个字节来表示肯定窗口大小肯定是不够的, 所以这里的窗口换成10进制之后需要乘128或者256才是实际的窗口大小.

为什么握手需要三次, 两次不可以吗?

不可以. 最主要的目的是为了防止server一直等待, 浪费资源.

为什么只有两次握手的时候Server会一直等待?

假设client发出的第一个连接请求报文段, 因为网络延迟, 在连接释放以后的某个时间才到达server

本来这是一个早已失效的连接请求, 但server收到此失效的请求后, 误认为是client再次发出的一个新的连接请求

于是server就向client发出确认报文段, 同意建立连接

如果不采用“3次握手”, 那么只要server发出确认, 新的连接就建立了

由于现在client并没有真正想连接服务器的意愿, 因此不会理睬server的确认, 也不会向server发送数据

但server却以为新的连接已经建立, 并一直等待client发来数据, 这样, server的很多资源就白白浪费掉了

◼ 采用“三次握手”的办法可以防止上述现象发生

例如上述情况, client没有向server的确认发出确认, server由于收不到确认, 就知道client并没有要求建立连接.

如果第三次握手失败怎么办?服务器进入同步已接受状态, 并重发, 重发超过次数阈值之后, 发送RST强制关闭这次连接.

释放连接——4次挥手

image-20221007231508646

4次挥手的过程解读:

​ 第一次挥手: 当客户端发送自己没有东西再发送给服务端时, 客户端发送连接释放请求, FIN=1, ACK=1, 告诉发送方我没有东西在发送给你了. 连接释放请求发送完成之后, 客户端就进入FIN-WAIT-1(终止等待状态) . 等待服务器响应第一次挥手的确认.

​ 第二次挥手: 服务端收到客户端释放连接的请求, 发送返回确认. 这时就会进入CLSOSE-WAIT(关闭等待状态) , 在这个状态下, 服务端就会考虑自己是否还有数据要发送给对方. 如果没有, 就会发送FIN报文(第三次挥手)给对方. 此时客户端接收到服务端返回的ACK确认就会进入FIN-WAIT-2(终止等待状态), 等待服务端发送的FIN报文(第三次挥手).

​ 第三次挥手: 服务端经过考虑之后, 确认自己没有东西再发送给客户端, 就会发送FIN=1的报文, 然后进入LAST-ACK状态. 这个状态是被动关闭一方在发送FIN之后, 最后等待对方的ACK报文.

​ 第四次挥手: 当接受到被动关闭的一方发送的FIN之后, 发送ACK确认. 发送之后, 进入时间等待, 等待一段时候时间之后, 彻底关闭. 被动关闭的一方接收到ACK确认, 直接关闭.

还有一种比较罕见的状态: CLOSING. : 表示你发送FIN报文后, 并没有接收到对方的ACK报文, 反而是接收到了对方的FIN报文. 这时因为双方想同时关闭造成的特殊状态.

**细节: **

1.TCP/IP的协议中, 允许任何一方先发起断开请求. 上图是客户端主动要求断开的.

2.主动关闭的一方发送了第四次挥手的ACK报文后, 有一个 TIME-WAIT(时间等待). 等待一段时间之后才真的关闭连接. 为的是确认服务器端是否收到客户端发出的 ACK 确认报文, 当客户端发出最后的 ACK 确认报文时, 并不能确定服务器端能够收到该段报文. 并且等待时间可以防止新建立起来的连接, 错误接收到FIN, 从而断开连接.

所以客户端在发送完 ACK 确认报文之后, 会设置一个时长为 2MSL 的计时器.

MSL 指的是 Maximum Segment Lifetime: 一段 TCP 报文在传输过程中的最大生命周期.

2MSL 即是服务器端发出为 FIN 报文和客户端发出的 ACK 确认报文所能保持有效的最大时长.

服务器端在 1MSL 内没有收到客户端发出的 ACK 确认报文, 就会再次向客户端发出 FIN 报文:

  • 如果客户端在 2MSL 内, 再次收到了来自服务器端的 FIN 报文, 说明服务器端由于各种原因没有接收到客户端发出的 ACK 确认报文.

客户端再次向服务器端发出 ACK 确认报文, 计时器重置, 重新开始 2MSL 的计时.

  • 否则客户端在 2MSL 内没有再次收到来自服务器端的 FIN 报文, 说明服务器端正常接收了 ACK 确认报文, 客户端可以进入 CLOSED 阶段, 完成“四次挥手”.

为什么释放连接需要4次挥手?

TCP是全双工模式, 主机1和主机2可以同时互相发送数据.

第1次挥手: 当主机1发出FIN报文段时

​ 表示主机1告诉主机2, 主机1已经没有数据要发送了, 但是, 此时主机1还是可以接受来自主机2的数据

第2次挥手: 当主机2返回ACK报文段时

表示主机2已经知道主机1没有数据发送了, 但是主机2还是可以发送数据到主机1的. 如果这时就彻底断开连接, 主机2就没有办法将剩余数据发送给主机1了.

第3次挥手: 当主机2也发送了FIN报文段时

​ 表示主机2告诉主机1, 主机2已经没有数据要发送了

第4次挥手: 当主机1返回ACK报文段时

​ 表示主机1已经知道主机2没有数据发送了. 随后正式断开

使用Socket编程抓包三次握手, 四次挥手.在抓包过程中, 有可能只看到三次挥手, 其实是第二次和第三次挥手合并了, 当被动关闭的一方收到第一次挥手的FIN时, 代表主动的一方没有数据要发给被动的一方了, 如果被动发送的一方也没有需要发送的数据了, 就会将第2次, 和第3次合并. 同时告诉主动方两件事.

长连接: 即使发完需要的数据, 还一直保持连接状态就是长连接. 经常需要进行频繁交互的就是使用长连接.

短连接: 发送完需要的数据, 就断开的连接就是短连接.