TCP的建立与终止——三次握手、四次挥手
目录
3.5 ISN(Initial Sequence Number)是固定的吗?
3.6 一个包数据可能会被拆成多个包发送,如何处理丢包问题?
4.3 如果已经建立了连接,但是客户端突然出现故障了怎么办?
1. UDP和TCP的区别
UDP:在传送数据前不需要先建立连接,远地的主机在收到UDP报文后也不需要给出任何确认。虽然UDP不提供可靠交付,但是正是因为这样,省去和很多的开销,使得它的速度比较快,比如一些对实时性要求较高的服务,就常常使用的是UDP,比如视频流、在线游戏和实时语音通话等。
TCP:提供面向连接的服务,在传送数据之前必须先建立连接,数据传送完成后要释放连接。因此TCP是一种可靠的的运输服务,但是正因为这样,不可避免的增加了许多的开销,比如确认,流量控制等。常应用于网页浏览、电子邮件、文件传输等。
二者的主要区别:
- 连接方式:TCP是连接导向,UDP是无连接
- 可靠性:TCP提供可靠传输,UDP不保证可靠性
- 数据传输:TCP面向字节流,UDP面向数据报
- 性能:UDP通常更快,但不保证数据的正确性和顺序,而TCP更稳定但相对较慢
2. TCP概述
TCP把连接作为最基本的对象,每一条TCP连接都有两个端点,这种断点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,例如,若IP地址为192.3.4.16 而端口号为80,那么得到的套接字为192.3.4.16:80。
3. TCP连接的建立(三次握手)
三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。
注意,三次握手必须是一方主动打开,另一方被动打开的
首先,TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,而客户端处于Closed状态,服务器处于LISTEN(监听)状态。
然后,进行三次握手:
1)第一次握手:首先客户端向服务器发送一段SYN(同步)报文,并指明客户端是初始化序列号是ISN,其中
- 首部的同步位为SYN=1,表示“请求建立新连接”;
- 序号为seq=x(x一般位1);
- SYN=1的报文段不能携带数据,但要消耗掉一个序号;
- 随后客户端进入SYN-SENT阶段。
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
注:SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号
2)第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。
在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。
不要将确认序号ack与标志位中的ACK搞混了
确认方ack=发起方Seq+1,两端配对
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
注:SYN+ACK报文也不能携带数据,但是同样要消耗一个序号
3)第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
注:ACK报文段可以携带数据,但是如果不携带数据则不消耗序号
3.1 为什么TCP客户端最后还要发送一次确认?
一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,此时,客户端认为自己只有一个连接,但是服务器认为有两个连接,导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
那么如何通过三次握手解决该问题?参考下图
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
- 如果一个「旧 SYN 报文」比「最新的 SYN」 报文早到达了服务端(一次握手),那么此时服务端就会回一个 SYN + ACK (二次握手)报文给客户端,此报文中的确认号是 91(90+1)。
- 客户端收到后,发现自己期望收到的确认号应该是 100 + 1,而不是 90 + 1,于是就会回 RST 报文(三次握手)。
- 服务端收到 RST 报文后,就会释放连接。
- 后续最新的 SYN 抵达了服务端后,客户端与服务端就可以正常的完成三次握手了。
当第三次握手失败时,服务器并不会重传ack报文,而是直接发送RST报文段,进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。
3.2 什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。简单来说就是,半连接队列用于存储那些已经通过了TCP三次握手的前两次握手(即接收了客户端的SYN,并回复了SYN-ACK)但还未完成第三次握手的连接。
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
全连接队列:已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
半连接队列的工作原理:
- 当客户端向服务器发送SYN请求(第一次握手)时,服务器会为这个请求分配资源,并将这个连接放入半连接队列中,同时向客户端回复SYN-ACK(第二次握手)。
- 客户端收到SYN-ACK后,回复ACK(第三次握手),服务器收到ACK后,才会将该连接从半连接队列移到全连接队列,并认为该连接已经完全建立,可以开始通信。
- 如果客户端没有在超时时间内回复ACK(如因网络延迟或故意攻击),服务器会在超时后将这个半连接从队列中移除。
针对上面最后一条,如果服务器发送完SYN-ACK包,未收到客户确认包,服务器将进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s……
3.3 半连接队列被填满或遇到SYN洪泛攻击是如何处理?
当半连接队列被填满或遇到SYN洪泛攻击时,服务器的TCP连接管理会受到严重影响。这种情况下,服务器可能无法正常接收新的连接请求,导致拒绝服务。
1)半连接队列被填满
半连接队列在上面的问题中已经详细说过,它是用于存储那些完成了TCP三次握手的前两次握手(SYN和SYN-ACK),但还未完成第三次握手(ACK)的连接,它的容量是有限的,由服务器的配置决定。
- 当客户端向服务器发送SYN请求时,服务器会将这个连接放入半连接队列,并向客户端发送SYN-ACK响应。
- 如果半连接队列中的连接数量超过了其容量上限,就表示队列已被填满。
- 当队列被填满后,服务器将无法处理新的SYN请求,因为它已经没有空间存储新的半连接。这时,服务器可能会丢弃新的SYN包,导致新的连接无法建立。
半连接队列填满的原因:
- 正常情况下:如果服务器的负载很高,并且有大量合法客户端同时尝试连接,半连接队列可能会短暂填满。
- 攻击情况下:在SYN洪泛攻击中,攻击者故意发送大量的SYN请求,并不回复ACK,以使半连接队列快速填满,从而导致服务器无法接受新的合法连接。
2)什么是SYN洪泛攻击?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。
SYN攻击利用TCP协议的三次握手过程来耗尽服务器的资源,Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
如何应对?
1)检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstat 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
2)SYN Cookie:当服务器检测到半连接队列即将被填满或受到SYN洪泛攻击时,通过使用SYN Cookie解决,在linux中通过tcp_syncookies参数来应对这件事:
- 服务器不在半连接队列中分配空间,而是将序列号经过加密算法生成一个Cookie,并发送给客户端。
- 客户端回复的ACK中会带上这个SYN Cookie。服务器通过这个Cookie验证连接的合法性,从而不依赖半连接队列的大小。
- 这种方式即使在遭受攻击时,也能确保服务器继续接收并验证新的连接请求。
千万别用 tcp_syncookies来处理正常的大负载的连接的情况。因为, SYN Cookie是妥协版的TCP协议,并不严谨。对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是: tcp_synack_retries可以用他来减少重试次数(服务器发出);第二个是: tcp_max_syn_backlog,可以增大SYN连接数;第三个是: tcp_abort_on_overflow处理不过来干脆就直接拒绝连接了。
3.4 三次握手过程中可以携带数据吗?
其实第三次握手的时候,理论上是可以携带数据的。但是,第一次、第二次握手不可以携带数据。
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态(服务器此时还未处于 ESTABLISHED 状态)。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
虽然第三次握手理论上可以携带数据,但通常不这样做,这是因为:
- 确保连接的稳定性:三次握手的主要目的是建立一个可靠的连接,以确保双方的发送和接收能力正常。如果在连接建立之前就传输数据,而连接建立失败,那么这些数据就可能丢失。
- 安全性:三次握手阶段的数据传输还没有经过完整的安全和可靠性验证,因此传输数据可能不安全。
- 协议规范:标准的TCP协议并不推荐在SYN和SYN-ACK包中携带数据,以减少复杂性,并确保握手的过程专注于连接建立。
例外:
虽然标准的三次握手过程不携带数据,但有一些TCP优化技术允许在三次握手的第三次握手(ACK包)中携带数据。这种优化被称为“TCP Fast Open”。
- TCP Fast Open (TFO) 是一种优化技术,可以在第三次握手中携带数据。这需要客户端和服务器都支持TFO,并且客户端在第一次握手时发送一个TFO Cookie。
- 当客户端和服务器在后续的连接中都使用这个Cookie时,客户端可以在三次握手的过程中携带数据,从而减少延迟。
总结:
TCP三次握手过程中客户端可以携带数据,但为了标准性一般不这么做,第一次、第二次握手不可以携带数据。但是在一些优化情况下(如TCP Fast Open),可以在第三次握手过程中携带数据,提高传输效率。
3.5 ISN(Initial Sequence Number)是固定的吗?
对于连接的3次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据).
当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1 。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。
三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
3.6 一个包数据可能会被拆成多个包发送,如何处理丢包问题?
在TCP协议中,当一个数据包过大或者需要适应网络传输情况时,数据可能会被拆成多个包发送,一般通过数据拆分与组装对数据进行处理。
- 当数据被拆分成多个TCP包发送时,TCP会为每个包分配一个序列号(Sequence Number),这些序列号用于标识每个包在整个数据流中的位置。
- 接收端根据序列号来重组这些包,以确保所有数据按照正确的顺序重新组装。
该序列号和握手过程中的ISN有紧密的关系,一旦三次握手完成,ISN就成为后续数据传输中序列号的基准点:
- 客户端发送的第一个数据包:客户端发送的第一个数据包的序列号是ISN_C + 1,这个“+1”是因为ISN本身被用来标识SYN包。
- 服务器发送的第一个数据包:同样,服务器发送的第一个数据包的序列号是ISN_S + 1。
- 从这个基准序列号(ISN + 1)开始,后续的数据包序列号会递增,增量为每个数据包的字节数。
下面介绍如何对数据包进行检测以及处理:
丢包的检测与处理机制:
a. 确认机制(ACK)
- 当接收端成功接收到一个数据包时,它会返回一个 确认包(接收端需要回复一个ACK=序列号+长度,确定该数据包已成功被接收)给发送端,告知其接收到的数据序列号。
- 如果发送端在一定的时间内(超时时间)没有收到某个数据包的ACK(接收端发送的ACK=序列号+长度,并发送端会将其作为下一数据包的起始序列号),就会认为该数据包可能丢失了
b. 超时重传(Timeout Retransmission)
- 当发送端没有在预定的超时时间内收到ACK时,它会认为这个数据包丢失了,并重传这个包。
- TCP使用动态超时重传机制,即根据网络的延迟情况调整超时时间,以提高丢包检测的效率。
c. 快速重传(Fast Retransmit)
- 如果接收端收到一个数据包,但发现中间的一个数据包丢失了(例如收到序列号为1、3、4的包,但序列号2的包丢失),它会重复发送对序列号2的重复确认(Duplicate ACK)。
- 当发送端收到连续的三个重复确认(Triple Duplicate ACKs)时,就会触发快速重传机制,立即重发那个丢失的数据包,而不必等待超时。
3.7 三次握手总结
三次握手建立连接的首要目的是同步序列号。只有同步了序列号才有可靠的传输,TCP 协议的许多特性都是依赖序列号实现的,比如流量控制、消息丢失后的重发等等,这也是三次握手中的报文被称为 SYN 的原因,因为 SYN 的全称就叫做 Synchronize Sequence Numbers
a. 客户端
客户端发送 SYN 开启了三次握手,之后客户端连接的状态是 SYN_SENT,然后等待服务器回复 ACK 报文。正常情况下,服务器会在几毫秒内返回 ACK,但如果客户端迟迟没有收到 ACK 会怎么样呢?客户端会重发 SYN,重试的次数由 tcp_syn_retries 参数(在3.3中详细说过该参数以及yncookies)控制,默认是 6 次:
net.ipv4.tcp_syn_retries = 6
第 1 次重试发生在 1 秒钟后,接着会以翻倍的方式在第 2、4、8、16、32 秒共做 6 次重试,最后一次重试会等待 64 秒,如果仍然没有返回 ACK,才会终止三次握手。所以,总耗时是 1+2+4+8+16+32+64=127 秒,超过 2 分钟。
如果这是一台有明确任务的服务器,可以根据网络的稳定性和目标服务器的繁忙程度修改重试次数,调整客户端的三次握手时间上限。比如内网中通讯时,就可以适当调低重试次数,尽快把错误暴露给应用程序。
b. 服务器
当服务器收到 SYN 报文后,服务器会立刻回复 SYN+ACK报文,既确认了客户端的序列号,也把自己的序列号发给了对方。此时,服务器端出现了新连接,状态是 SYN_RCVD。这个状态下,服务器必须建立一个 SYN 半连接队列(3.2讲过)来维护未完成的握手信息,当这个队列溢出后,服务器将无法再建立新连接(3.3详细说过)。
如果 SYN 半连接队列已满,只能丢弃连接吗?并不是这样,开启 syncookies 功能就可以在不使用 SYN 队列的情况下成功建立连接。syncookies 是这么做的:服务器将序列号经过加密算法生成一个Cookie,放在己方发出的 SYN+ACK 报文中发出,当客户端返回 ACK 报文时(客户端回复的ACK中会带上这个SYN Cookie),取出该值验证,如果合法,就认为连接建立成功
图片来源:https://blog.csdn.net/kking_edc/article/details/109480520
4. 四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手,这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力(比如服务器将自己的接收端关闭后,仍可以接收来自客户端的发送请求,只不过服务器无法进行回传)。
TCP 连接的断开一共需要发送四个包,因此称为四次挥手。数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,这里假设客户端主动关闭,服务器被动关闭。
1)第一次挥手(主动->被动):客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
即发出连接释放报文段(FIN=1,序号seq=u,u等于前面已经传送过来的数据的最后一个字节的序号加1),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)第二次挥手(被动->主动):服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,并且带上自己的序列号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE_WAIT状态持续的时间。
客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段(在这之前还需要接受服务器发送的最后的数据)。
3)第三次挥手(被动->主动):如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
4)第四次挥手(主动->被动):客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL(最长报文段寿命)后,客户端才进入CLOSED状态。
服务器结束TCP连接的时间要比客户端早一些。
四次挥手步骤如下:
图片来源:https://gitcode.csdn.net/66c55ce4a0bc797cf7b649bd.html?dp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mjg0NzQ0NCwiZXhwIjoxNzI5NzM2OTYyLCJpYXQiOjE3MjkxMzIxNjIsInVzZXJuYW1lIjoibTBfNjMwODYxOTgifQ.IdWGuw8i52y5Bl_ybIBCykU8UlkEILXf5ePPFEIFa3g&spm=1001.2101.3001.6650.1&
4.1 为什么客户端最后还要等待2MSL?
MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态
总结,需要等待2MSL的原因:
第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
第三,如果主动方不保留TIME_TAIT状态,而是直接CLOSE,那么此时连接的端口恢复了自由身,可以复用于新连接。然而,被动方的 FIN 报文可能再次到达,这既可能是网络中的路由器重复发送,也有可能是被动方没收到 ACK 时基于 tcp_orphan_retries 参数重发。这样,正常通讯的新连接就可能被重复发送的 FIN 报文误关闭。保留 TIME_WAIT 状态,就可以应付重发的 FIN 报文,当然,其他数据报文也有可能重发,所以 TIME_WAIT 状态还能避免数据错乱。
为什么是2MSL不是1.5MSL或者其他值呢?
TIME_WAIT确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发Fin,一来一去正好2个MSL .
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
4.2 为什么建立连接是三次握手,关闭连接确是四次挥手?
1)这是因为 TCP 不允许连接处于半打开状态时就单向传输数据,所以在三次握手建立连接时,服务器会把 ACK 和 SYN 放在一起发给客户端,其中,ACK 用来打开客户端的发送通道,SYN 用来打开服务器的发送通道。这样,原本的四次握手就降为三次握手了。
但是当连接处于半关闭状态时,TCP 是允许单向传输数据的。为便于理解,我们把先关闭连接的一方叫做主动方,后关闭连接的一方叫做被动方。当主动方关闭连接时,被动方仍然可以在不调用 close 函数的状态下,长时间发送数据,此时连接处于半关闭状态。这一特性是 TCP 的双向通道互相独立所致,却也使得关闭连接必须通过四次挥手才能做到。
四次挥手的简单步骤:
- 在TCP连接中,关闭连接是一个双向的过程,客户端和服务器都需要分别关闭各自的发送通道
- 当客户端发送FIN请求关闭发送通道时,服务器可能还有数据要发送,因此它会先回复ACK,并在数据发送完毕后再发送FIN
- 客户端收到服务器的FIN后,再发送最后一个ACK确认,这样才能确保连接彻底关闭
简单来说,三次握手是因为只需要确认双方的发送和接收能力即可,但四次挥手需要双方各自关闭发送通道,这个过程需要双方都能确认所有数据都已传输完毕,并且各自关闭了发送和接收通道。
2)建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
4.3 如果已经建立了连接,但是客户端突然出现故障了怎么办?
如果连接已经建立,但客户端突然故障,服务器不会立即发现,它会持续等待客户端的数据。但TCP设有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
参考:
原文地址:https://blog.csdn.net/m0_63086198/article/details/143020574
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!