本文将介绍六个标记位,与详解三次握手四次挥手
一. 6 个标志位
虽然有的 TCP 标准包含 8 个标记位,但我们主要学习其中 6 个最常用的。
上面说过数据段在来回通信的时候,有的是正常的数据报文,有的是确认报文。 这里我们就可以理解tcp报文也是有类型的!
在 TCP 通信过程中,数据段有时是正常的数据报文,有时是确认报文。为了区分不同类型的报文,TCP 使用 6 个标志位来表示不同类型的报文。
站在服务器的角度它一定会收到各种各样的tcp报文!所以接收方要根据不同的tcp报文,要有不同的处理动作!
- SYN 标记位 SYN 标记位用于表示连接请求报文,请求建立连接。默认情况下,SYN 标记位为 0,只有在建立连接时该标记位才会设置为 1。携带 SYN 标识的报文称为同步报文段。
- FIN 标记位 FIN 标记位用于表示断开连接请求报文,默认情况下该标记位为 0,当置为 1 时表示这是一个断开连接的请求。携带 FIN 标识的报文称为结束报文段。
- ACK 标记位 ACK 标记位用于表示确认应答报文。当通信双方进行数据传输时,客户端发出请求,服务器返回确认,确认报文的 ACK 标记位会被置为 1。即使在正常的数据传输报文中,若该报文包含确认能力,ACK 也会置为 1。三次握手之后,几乎所有的报文都带有 ACK 标记。
- PSH 标记位 PSH 是 PUSH 的简写,用于催促接收方尽快将数据从接收缓冲区取走。当接收方上层处理数据较慢,导致接收缓冲区积压数据时,发送方可能会发送一个 PSH 报文,要求接收方立即将数据取走。 如何催促呢?多路转接我们在理解
- URG 标记位****(紧急插队) URG 标记位用于表示紧急数据。当报文中包含需要紧急处理的数据时,URG 标记位会置为 1,并且使用16 位的紧急指针来标识紧急数据在有效载荷中的位置。紧急数据会被优先处理,不需要排队。
将URG标志位置为1 ,表明这个报文中的有效载荷是涵盖有紧急数据的,注意我并没有说报文中有效载荷都是紧急数据!
- 那这个数据在哪里呢?
这个时候就有16位紧急指针来标识,16位紧急指针表达的是在有效载荷中的偏移量。假如紧急指针写个20,也就是说该报文中有效载荷偏移量为20的数据开始是要紧急处理的!
- 现在这个紧急指针偏移量我知道 ,那这个紧急数据到那结束呢难道到有效载荷的结尾?
并不是,根据16位紧急指针找到偏移量以字节位单位,往后读取一个字节就是紧急数据。紧急数据不需要排队直接被上层读取,一般这个URG这个****1字节数据也成为带外数据。
带外数据并不是tcp帮我们主动弄这个功能,而是tcp提供这个功能供上层选择,我们自己在写服务器的时候可以自己选择正常读数据之前有没有带外数据
设置这个就可以读写带外数据
应用场景:
- 紧急命令:在文件传输或数据流传输过程中,如果发送端需要立即通知接收端一个紧急命令或状态变化(如取消操作),可以在数据包中设置URG标志,以指示紧急数据的位置。
- 例如连接超时时,紧急获取一些数据
- 实时系统:在实时系统中,如远程手术或自动驾驶车辆控制,某些指令需要立即处理,URG标志可用于标记这些紧急指令。
6.RST 标记位
RST是reset的简写。在写套接字TCP协议的时候我们曾经说过,通信双方在通信之前必须要把三次握手建立好才能进行通信。
应用场景分析:
❓ 三次握手建立连接,三次握手一定能保证握手成功吗?不一定!
这个世界上没有100%一定成功的,并且我们也知道三次握手最后一次ACK是没有应答的,可能会出现握手失败的情况。
同理四次挥手也一样!人家只是在TCP这里设立了建立连接三次握手、断开连接四次挥手,但可没说一定成功。
- 但是能保证只要把三次握手四次挥手走完就保证算你连接建立成功和断开连接成功。
- 其次,即便是连接建立成功了,我们通信过程中也有可能出现单方面出现问题!如服务器电源拔掉了。
- 然后插上电之后服务器重启了,但是现在这个服务器操作系统并没有意识到历史还断连接的,虽然这个连接在物理就被干掉了。
❓ 但是客户端知不知道服务器重启过呢?并不知道,你服务器又没有给我四次挥手。
- 所以就可能会存在client认为连接还存在,服务器认为连接不存在。
如果client认为连接还存在会出现什么问题?是不是就直接发报文了。
- 可是报文是有类型,就注定了这个报文不会携带SYN,服务器收到这个报文很奇怪,我和你并没有建立连接,我们协议规定好我们通信之前要先建立连接,客户端你直接把数据发过来了。
- 所以服务器此时直接给客户端回一个报文,而这个报文回携带RST标记位。告诉客户端这个连接出异常了,你关闭现在的连接然后重新和我建立连接吧!
- RST:** 对方要求重新建立连接。我们把携带RST标识的称为复位报文段。有了RST标记位,双方连接建立一方认为成功一方认为不成功,那么后序在通信的时候,认为不成功的一方就把连接重置了。**
到目前为止我们已经把TCP报头都学完了。自己也可以把选项部分可以看一看。
二.连接管理机制
(这个导图是第二遍做了 qwq,第一遍做的重点都标红了,更漂亮,结果没来得及保存就断电了...买的电脑电池还没到,之后写一篇文章将如何给电脑换电池叭,最有学习动力的一集(o-ωq)).oO
TCP连接与握手、挥手详解
1. TCP连接的建立与断开
- 三次握手:TCP在正常情况下要经过三次握手来建立连接。
- 四次挥手:TCP在正常情况下要经过四次挥手来断开连接。
2. 连接的理解
- 大量连接:未来可能会有大量的客户端(Client)连接到服务器(Server),因此服务器端会存在大量的连接。
- 操作系统管理:操作系统(OS)需要管理这些连接,包括连接的状态、异常等。
- 数据结构:每个连接本质上是内核中的一种数据结构。建立连接成功时,会在内存中创建对应的连接对象,并通过某种数据结构组织多个连接对象。所以先描述,再组织
- 成本:维护连接是有成本的,包括CPU和内存资源。
三次握手
- 目的:双方通信之前必须先建立连接,经历三次握手。
- 所谓的三次握手就是双方有来有回的吞吐了三个报文
- SYN报文:第一个报文中只有报头,SYN置为1,表示连接请求。
- SYN+ACK报文:第二个报文中SYN和ACK都置为1,表示同意建立连接并确认上一个报文。
- ACK报文:第三个报文中只有ACK置为1,确认上一个报文。
- 状态变化:
- 客户端发送SYN后,状态变为
SYN_SENT
。- 服务器收到SYN后,状态变为SYN_RCVD
。- 客户端收到SYN+ACK后,状态变为ESTABLISHED
。- 服务器收到ACK后,状态也变为ESTABLISHED
。
- 客户端发送SYN后,状态变为
1. 三次握手的可靠性
(关于三次握手关注问题的角度
- 不一定成功:三次握手过程中,最后一个ACK可能丢失,但有超时重传机制。
- 三次握手是建立连接的机制,可没有说一定能保证握手成功。包括四次挥手也是一样的。
- 超时重传:如果最后一个 ACK丢失,服务器没有收到应答,会触发超时重传。
- RST报文:如果客户端认为连接已建立但服务器未收到ACK,服务器会返回RST报文让客户端重置连接。
- 客户端和服务器连接是要被OS管理起来的,先描述,在组织。但维护一个连接是有成本的。
- 对服务器资源被占用的最小化
2. 1/2/3 次握手对比
- 一次握手:不可行,会导致SYN洪水攻击。占用了大量服务器资源
- 二次握手:不可行,客户端未收到ACK时,服务器可能认为连接已建立(浪费了服务器的资源
- 三次握手:最小成本验证全双工通信信道通畅,防止单主机对服务器进行攻击。
三次握手的好处⭕
1. 服务器是否会受到SYN洪水攻击
问题:三次握手能否完全防止服务器受到SYN洪水攻击?
回答:
- 服务器受到攻击本质上不是通过TCP握手来解决的。如果攻击者使用多台机器进行攻击,即使有三次握手机制,服务器仍然可能受到SYN洪水攻击。- 如果服务器存在明显漏洞,那么这是服务器自身的问题。- 三次握手能够显著减少SYN洪水攻击的影响,但其初衷是为了避免连接建立过程中的明显漏洞,而不是专门针对SYN洪水攻击设计的。
2. 三次握手才可以阻止重复历史连接的初始化
- 假设有这样一种场景,客户端给服务端发送了一个SYN报文(seq=100),但是这个报文由于网络波动阻塞了,于是客户端又重新发送了一个新的SYN报文(seq=200),注意不是重传,重传的SYN的序列号是一样的。
从上图可以看出,当客户端发送的SYN报文被网络阻塞后,再次发送新的SYN报文,由于旧的报文比新的报文先抵达服务端,服务端肯定会回一个SYN+ACK报文给客户端,此时客户端就可以根据这个报文来判断这是一个历史连接,由此客户端就会发送一个RST报文,要求断开此次连接(即历史连接)。等新的SYN报文抵达服务端后,才会正式的建立连接。
如果是两次握手,那就不能阻止历史连接
- 当服务端收到客户端发来的SYN报文后,就进入了ESTABLISHED状态,这就意味着服务端此时就可以给客户端发送数据了,但是客户端还没有进入ESTABLISHED状态,必须收到服务端的SYN+ACK报文后,才会进入ESTABLISHED状态。- 可以看出,当服务端收到第一个SYN报文后(旧的)就已经建立了连接(服务端并不知道这是历史连接),并且在回复给客户端的报文中携带了数据,但是客户端通过收到的SYN+ACK报文,发现这是历史连接;对于客户端来说,它根本来不及在服务端给客户端发送数据前来阻止这个历史连接,导致这个历史连接被创建,服务端白白发送了数据(数据创建是需要时间和空间的),造成资源浪费;只有在收到客户端发来的RST报文后才会断开连接。
因此,要解决这样的问题,客户端就必须在服务端发送数据之前来阻止掉这个历史连接,而要实现这个功能就需要三次握手。
3. 三次握手才可以同步双方的初始化序列号
⭕TCP协议通信的双方,都必须要维护序列号,序列号是实现可靠传输的一个关键因素,其作用如下:
- 接收端可以根据序列号进行重复数据的去重。
- 接收端可以根据序列号按序接受。
- 通过ACK报文中的序列号可以识别发出去的数据包中,哪些已经被对方收到了。
- 当客户端给服务端发送SYN(携带着自己的序列号)报文的时候,需要服务端回一个ACK应答报文,表明客户端的SYN报文已经成功接收;同样,在这个报文中除了ACK应答号还有服务端自己的序列号(发送的是SYN+ACK报文),也需要客户端回一个ACK应答报文,来确保服务端的SYN被成功接收;这样一来一回就能保证双方的初始化序列号被可靠同步。
- 从下图可以看出:
- 四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了三次握手。- 而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
思考
1. 四次、五次或更多次握手是否可行
四次握手:
- 四次握手也能验证全双工通信,但存在一个问题:最后一次ACK是由服务器发送的。这意味着服务器必须先完成连接建立,这与两次握手的问题类似。偶数次握手都会面临这个问题,即最后一方需要先完成连接建立。
五次、六次等更多次握手:
- 奇数次握手(如五次、七次等)在理论上是可行的,因为它们可以像三次握手一样验证全双工通信。
- 但是,从实际应用和效率的角度来看,三次握手已经足够有效,没有必要增加更多的握手次数。三次握手已经能够满足需求,再增加握手次数只会增加复杂性和延迟。
2. 三次握手的理解
三次握手的另一种解释:
- 三次握手实际上也可以理解为四次握手,因为在第二次握手中,SYN和ACK通常是合并在一起发送的。
- 在正常情况下,SYN和ACK应该是两个独立的报文,但由于效率考虑,通常将它们合并成一个报文发送。
3. 为什么要连接?
- 保证可靠性:连接本身不直接保证可靠性,但间接支持可靠性特性,如超时重传、流量控制、拥塞控制等。
- 连接结构体:三次握手帮助创建连接结构体,存储连接状态和相关参数。
4. UDP不需要连接的原因
- 无需维护状态:UDP不需要维护双方通信状态,因此也不需要握手。
四次挥手
1.理解
- 断开连接是双方的事,又因为tcp是全双工的,所以****需要征得双方同意。
- 过程:
- 客户端发送FIN,表示不再发送数据,进入
FIN_WAIT_1
状态。- 服务器收到FIN后,发送ACK,进入CLOSE_WAIT
状态。- 服务器发送FIN,表示不再发送数据,进入LAST_ACK
状态。- 客户端收到FIN后,发送ACK,进入TIME_WAIT
状态。
- 客户端发送FIN,表示不再发送数据,进入
三次挥手:如果服务器在收到FIN的同时也要关闭连接,可以将FIN和ACK合并发送,从而变成三次挥手。
2. 特殊状态
- 主动断开连接的一方,最终状态是TIME_WAIT状态。****被动断开连接的一方,两次挥手完成,会进入CLOSE_WAIT状态。
- 保持CLOSE_WAIT状态:如果服务器不调用close,则不会发送FIN,一直保持
CLOSE_WAIT
状态。
无论是主动还是被动和是客户端或者是服务器没关系,因为TCP是地位对等的协议。
❓ 如何让服务器一直处于CLOSE_WAIT状态,不继续往下走?
让服务器不要调用close!那服务器只是被动触发完成两次挥手,因为不会调用close所以也不会给客户端发送FIN也就不会进入LAST_ACK状态。服务器一直处于CLOSE_WAIT状态。
我们可以把以前代码拿过来修改,在下篇文章中验证一下这个场景~
状态转换总结
客户端状态转换
- CLOSED -> SYN_SENT: 客户端调用 connect 发起连接请求。
- SYN_SENT -> ESTABLISHED: 收到服务器的 SYN+ACK 确认后,进入 ESTABLISHED。
- ESTABLISHED -> FIN_WAIT_1: 客户端调用 close 发起断开连接请求。
- FIN_WAIT_1 -> FIN_WAIT_2: 收到服务器对 FIN 的确认后,进入 FIN_WAIT_2。
- FIN_WAIT_2 -> TIME_WAIT: 收到服务器的 FIN,发送最后的 ACK,进入 TIME_WAIT。
- TIME_WAIT -> CLOSED: 等待 2 * MSL 后,进入 CLOSED 状态。
服务器端状态转换
- CLOSED -> LISTEN: 调用 listen 进入监听状态。
- LISTEN -> SYN_RCVD: 收到客户端的 SYN 后,发送 SYN+ACK,进入 SYN_RCVD。
- SYN_RCVD -> ESTABLISHED: 收到客户端的 ACK 后,进入 ESTABLISHED。
- ESTABLISHED -> CLOSE_WAIT: 收到客户端的 FIN 后,发送 ACK,进入 CLOSE_WAIT。
- CLOSE_WAIT -> LAST_ACK: 处理完数据后,发送 FIN,进入 LAST_ACK。
- LAST_ACK -> CLOSED: 收到客户端的 ACK 后,进入 CLOSED。
下篇文章将通过代码实验,对网络状态进行测试与查看,和对到思维导图中的后半部分继续讲解~
版权归原作者 lvy- 所有, 如有侵权,请联系我们删除。