为什么建立连接是三次握手断开连接是四次挥手?
三次握手的流程和四次挥手的流程是什么?
三次握手与四次回收分别对应TCP连接与断开过程
tcp报文格式
标志位含义
ACK:确认序号有效。
SYN:发起一个新连接。
FIN:释放一个连接。
- 1
- 2
- 3
三次握手的过程
注意:三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的
所谓三次握手,即建立TCP连接,需要客户端和服务端总共发送至少三个包确认连接的建立。流程如下
第一次握手
Client将标志位SYN置1,随机产生一个值seq=J,并将数据包发给Server
Client进入SYN_SENT状态,等待Server确认
第二次握手
Server收到数据包后标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置1,随机产生一个值,并将数据包发给Client确认连接请求,Server进入SYN_RCVD状态
第三次握手
Client收到确认后若ACK为1,则将该数据包发送给Server,Server检查ACK为1则连接建立成功,Client与Server进入ESTABLISHED状态完成三次握手,可以传输数据
问题1:为什么建立连接是三次握手,四次不可以吗
第一次握手:
Client什么都不能确认
Server确认了对方发送正常
- 1
- 2
第二次握手:
Client确认:自己发送/接收正常,对方发送/接收正常
Server确认:自己接收正常 ,对方发送正常
- 1
- 2
第三次握手:
Client确认:自己发送/接收正常, 对方发送/接收正常
Server确认:自己发送/接收正常,对方发送/接收正常
- 1
- 2
所以通过三次握手确认双方收发功能都正常,四次也可以但是显得比较多余。
TCP三次握手,如果两次握手会怎么样
设计上的缺陷
有这样一种情况,当A发送一个消息给B,但是由于网络原因,消息被阻塞在了某个节点,然后阻塞的时间超出设定的时间,A会认为这个消息丢失了,然后重新发送消息。
当A和B通信完成后,这个被A认为失效的消息,到达了B
对于B而言,以为这是一个新的请求链接消息,就向A发送确认,
对于A而言,它认为没有给B再次发送消息(因为上次的通话已经结束)所有A不会理睬B的这个确认,但是B则会一直等待A的消息
这就导致了B的时间被浪费(对于服务器而言,CPU等资源是一种浪费),这样是不可行的,这就是为什么不能两次握手的原因了。
所以有了三次握手的修订
第三次握手看似多余其实不然,这主要是为了防止已失效的请求报文段突然又传送到了服务端而产生连接的误判
四次挥手的过程
所谓四次挥手:即终止TCP连接,需要客户端和服务端总共发送4个包确认连接的断开。流程如下
第一次挥手:
Clien发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:
Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。
第三次挥手:
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:
Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次握手。
问题:为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
建立连接
因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
关闭连接
当收到对方的FIN报文时,仅表示对方不再发送数据但还能接收收据,我们也未必把全部数据都发给了对方,所以我们可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方表示同意关闭连接。因此我们的ACK和FIN一般会分开发送。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图
为什么需要TIME_WAIT状态
1、可靠的终止TCP连接
2、保证让迟来的TCP报文段有足够的时间被识别并丢弃
1)为实现TCP这种全双工连接的可靠释放
这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
2)为使旧的数据包在网络因过期而消失
每个具体TCP实现必须选择一个报文段最大生存时间MSL。它是任何报文段被丢弃前在网络内的最长时间。
TCP 三次握手的目的是什么?为什么不用两次和四次?
TCP 三次握手的主要目的是防止失效的连接请求报文被服务端接受
如果只有两次握手,假设当客户端发送第一次连接请求由于网络拥塞的原因,迟迟未到服务端,客户端没接收到确认报文,认为服务端没有收到,于是重新发送请求报文并与服务端建立连接,等这次连接断开了,之前滞留的那个请求报文又到达了服务端,就会让服务端与客户端再次连接成功,这时服务端就会一直等待客户端发送请求,造成了资源的浪费。
两次握手只能保证单向链路是可以通信的,理论上来说,要保证双向链路可以通信需要四次握手,但实际上服务端给客户端的 SYN 和 ACK 数据包可以合为一次握手,所以实际上只需要三次握手即可。
加问:那挥手为什么需要四次呢?三次不行吗?
答:挥手阶段中服务端的 ACK 和 FIN 数据包不能合为一次。因为挥手阶段的流程为客户端发送FIN数据包表示自己发完了,服务端立即回复 ACK 数据包表示自己知道了,此时客户端到服务端的连接已经释放了,客户端不会再发送数据了,但服务端还可以继续向客户端发送数据,等到服务端也完成了数据发送,才会发送 FIN,这时客户端回复 ACK,就可以结束通信了。
加问:TCP 在四次挥手的过程中为什么客户端最后还要等待 2MSL(Maximum Segment Lifetime)?
答:因为客户端要保证他的 ACK 包顺利到达服务端,如果客户端的ACK数据包丢失,则服务端或重新发送 FIN 包到客户端,而这两个过程的最长时间为 1MSL,加起来为 2MSL,如果 2MSL 后客户端还没有收到服务端重发的 FIN 包,则说明 ACK 包顺利到达,可以关闭连接了。
TCP 在握手阶段怎么管理客户端的连接?
TCP 在握手阶段服务端维护了两个队列:半连接队列和全连接队列
在客户端发起第一次握手时,服务端会把此请求放入半连接队列,并回复 SYN+ACK
在客户端回复 ACK,也就是第三次握手时,服务端将此连接加入到全连接队列
如果全连接队列满,则服务端的处理方式和 tcp_abort_on_overflow 参数的设置有关,如果该参数为 0,则丢弃该 ACK,如果为 1 则发送 RST 到客户端,直接放弃此次连接。
此条是我在了解DDOS时发现的,并非常考点,SYN Flood 攻击时会造成服务端的半连接队列被占满,从而影响到服务。
TCP 通过哪些方式来保证数据的可靠性?
TCP 保证数据可靠性的方式大致可以分为三类:
在数据包层面:校验和
在数据包传输层面:序列号、确认应答、超时重传
在流量控制层面:拥塞控制
校验和
计算方式:在数据传输的过程中,将发送的数据段都当做一个 16 位的整数。将这些整数加起来。并且加上进位,最后取反,得到校验和。
TCP 与 UDP 校验方式相同
序列号、确认应答、超时重传
在数据包传输的过程中,每个数据包都有一个序列号,当数据到达接收方时,接收方会发出一个确认应答,表示收到该数据包,并会说明下一次需要接收到的数据包序列号(32 位确认序列号)。如果发送端在一段时间内(2RTT 没有收到确认应答,则说明可能是发送的数据包丢失或者确认应答包丢失,此时发送端会进行数据包重传。
但发送端并不是一定要等到接收到上一个数据包的确认应答再发送下一个数据包,TCP 会利用窗口控制来提高传输速度,在一个发送窗口大小内,不用一定要等到应答才能发送下一段数据,发送窗口大小就是无需等待确认而可以继续发送数据的最大值。而发送窗口的大小是由接收端的接受窗口的剩余大小和拥塞窗口来决定的。(TCP 会话的双方都各自维护一个发送窗口和一个接收窗口)
拥塞控制
发送端维持一个叫做拥塞窗口 cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送端让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。
TCP 的拥塞控制主要是采用慢启动以及增性加,乘性减的机制,TCP一开始将拥塞窗口设置的很小,在逐渐经过一段时间的指数增长后超过门限,进入增性加阶段,此时窗口大小的增长是线性的,比之前的指数增长要慢很多,而当发生网络拥塞时,拥塞窗口大小直接减半(乘性减)。
TCP 长连接和短连接有什么区别?
TCP 短连接是指客户端与服务端连接后只进行一次读写就关闭连接,一般是客户端关闭。
而长连接则是指在进行完一次读写后不关闭连接,直到服务端压力过大则选择关闭一些长时间为进行读写的连接。
TCP 短连接的优点在于管理简单,而且不会对服务端造成太大的压力,而缺点是每次读写都需要连接耗时较长。
TCP 长连接的优点是可以迅速进行多次读写,缺点是对服务端压力大,且容易被恶意连接影响服务。
长短连接的区别就在于客户端和服务端选择的关闭策略不同,具体需要根据应用场景来选择合适的策略。
TCP 粘包、拆包及解决方法?
TCP 之所以会产生粘包和拆包拆包问题,是因为他本身就是一种字节流协议,TCP 本身就没有数据包的概念,需要发送和接受的数据是没有格式的,以字节流的形式传输,而在传输过程中会被分割为一段段数据块,也就是报文。TCP 要发送的数据会被先放置在数据缓冲区,接收数据也是从缓冲区获取,而缓冲区的大小即为最大报文长度,如果需要发送的数据长度大于缓冲区剩余的大小或者大于最大报文长度,则会出现拆包,如果是需要发送的数据很少,而短时间内又有其他数据包需要发送,就会出现粘包的现象。
解决方案有很多种,可以在数据包头加上数据包长度,或者把每个数据包封装为固定长度,不够则补 0,以及可以使用特定分割符号等等
我们在项目中也遇到过这种问题,因为我们在做流量检测的时候,有时候难以找到恶意软件的流量特征,会把数据包长度当做特征来使用,有些恶意软件内部无论会把这些数据包长度写死,这样恶意软件本身就不存在有无法解析粘包和拆包的情况,但对于我们来说,检测就会遇到障碍,尤其是攻击者可以设置 MSS 来使得数据包长度改变,对于这种攻击我们目前也没有很好的方案来解决。
参考链接
一文详解TCP
C++工程师面试宝典之计算机网络
网络基础:TCP协议-如何保证传输可靠性
解析TCP之滑动窗口
TCP握手中的半连接队列和全连接队列
TCP长连接与短连接的区别
TCP粘包,拆包及解决方法
作者:weak chicken
链接:https://leetcode-cn.com/circle/discuss/aqTOW4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。