TCP的三次握手和四次挥手

HTTP协议是建立在TCP协议之上的一种应用。
TCP位于传输层,HTTP位于应用层,TCP提供了可靠的字节流服务;
什么叫字节流服务呢?将大块数据分割成报文段为单位的数据包进行管理
TCP可以把数据可靠的传送给对方
为了确保数据能够准确无误的传送给目标处,tcp采用了—三次握手策略

网络分层模型图
在这里插入图片描述

TCP的三次握手

建立一个TCP连接时,需要客户端和服务器总共发送3个包。
进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常。
实质上其实就是连接服务器指定端口,建立TCP连接,指定自己的初始化序列号ISN(Initial Sequence Number)为后面的可靠性传送做准备,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

三次握手流程

1.客户端发个请求“开门呐,我要进来”给服务器
2.服务器发个“进来吧,我去给你开门”给客户端
3.客户端有很客气的发个“谢谢,我要进来了”给服务器

在这里插入图片描述
状态解释:
SYN SEND:发送连接请求后,等待匹配的连接请求
SYN RECEIVED:在收到和发送一个连接请求后等待连接请求的确认
ESTABLISHED:代表一个打开的连接,数据可以传送给用户

–同步SYN:
连接建立时用于同步序号,发起一个新连接。当SYN=1,ACK=0时表示:这是一个连接请求报文段。
若同意连接,则在响应报文段中使得SYN=1,ACK=1。
因此,SYN=1表示这是一个连接请求,或连接接受报文。
SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
–序列号seq:
占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;。
–确认ACK:
占1位,仅当ACK=1时,确认号ack字段才有效。ACK=0时,确认号ack无效
–确认号ack:
占4个字节,ack序号。占 4 字节,期望收到对方下个报文段的第一个数据字节的序号,只有ACK=1时,确认序号字段才有效,ack=Seq+1。

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
1、由客户端发送建立TCP连接的请求报文,其中报文中包含seq序列号,是由发送端随机生成的(客户端的初始化序列号 ISN©),并且将报文中的SYN字段置为1,表示需要建立TCP连接。(SYN=1,seq=x,x为随机生成数值)SYN:同步序列编号(Synchronize Sequence Numbers),SYN=1的报文段不能携带数据,但要消耗掉一个序号,此时客户端处于 SYN_SEND 状态。
2、服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,。
在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y,此时服务器处于 SYN_REVD 的状态。
3、客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

如果在握手过程中某个阶段莫名中断了,tcp协议会再以相同的顺序发送同样的数据包

一些常见的问题:

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

弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。

试想如果是用两次握手,则会出现下面这种情况:

如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。

什么是半连接队列?

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。

当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

ISN(Initial Sequence Number)是固定的吗?

ISN–seq的初始化
TCP初始化序列号不能设置为一个固定值,因为这样容易被攻击者猜出后续序列号,从而遭到攻击。
当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1 。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。

三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

三次握手过程中可以携带数据吗?

其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据

为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。

也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

SYN攻击是什么?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

四次挥手流程

在这里插入图片描述
1.客户端发个“时间不早了,我要走了”给服务器,等服务器起身送他
2.服务器听到了,发个“我知道了,那我送你出门吧”给客户端,等客户端走
3.服务器把门关上后,发个“我关门了”给客户端,然后等客户端走
4.客户端发个“我知道了,我走了”,之后自己就走了
【注意】中断连接端可以是Client端,也可以是Server端。

第一次挥手:
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:
Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
第三次挥手:
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

FIN-WAIT-1:等待远程TCP的连接中断请求,或先前的连接中断请求的确认
FIN-WAIT-2:从远程TCP等待连接中断请求
CLOESE WAIT:等待从本地用户发来的连接中断请求
CLOSING:等待远程TSP对中断请求的确认
LAST-ACK:等待原来发向远程TCP的连接中断请求的确认
TIME WAIT:等待足够时间以确保远程TCP接收到连接中断请求的确认
CLOSED:没有任何连接状态
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接

需要注意的问题:TIME_WAIT累积与端口耗尽
TIME_WAIT 端口耗尽是很严重的性能问题,会影响到性能基准。
➢ 当某个TCP 端点关闭TCP 连接时,会在内存中维护一个小的控制块, 用来记录最近所关闭连接的IP 地址和端口号。这类信息只会维持一小 段时间,通常是所估计的最大分段使用期的两倍(称为2MSL,通常为 2 分钟8)左右,以确保在这段时间内不会创建具有相同地址和端口号 的新连接。实际上,这个算法可以防止在两分钟内创建、关闭并重新 创建两个具有相同IP 地址和端口号的连接。
➢ 进行性能基准测试时,通常只有一台或几台用来产生流量的计算机连 接到某系统中去,客户端每次连接到服务器上去时,都会获得一个新 的源端口,以实现连接的唯一性。但由于可用源端口的数量有限(比 如,60 000 个),而且在2MSL 秒(比如,120秒)内连接是无法重 用的,连接率就被限制在了60 000/120=500 次/ 秒。如果再不断进 行优化,并且服务器的连接率不高于500 次/ 秒,就可确保不会遇到 TIME_WAIT 端口耗尽问题。
➢ 要修正这个问题,可以增加客户端负载生成机器的数量,或者确保客 户端和服务器在循环使用几个虚拟IP 地址以增加更多的连接组合。

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值