详解TCP三次握手和四次挥手

23 篇文章 0 订阅

先画出整体流程图,以及每一步发生变化时所调用的socket API

三次握手

第一次握手:客户端发送位码为syn = 1,随机产生seq number = 10001的数据包到服务器, 此时服务端由SYN = 1知道,客户端此时要求建立连接,此时客户端的状态为SYN_SENT

第二次握手:服务端收到请求后要确认连接信息,向客户端发送ack number = (客户端的seq + 1),syn = 1,ack = 1,随机产生seq = 20001的包,此时服务端状态由listen状态变为SYN_RECV

第三次握手:客户端收到后检查ack number是否正确,即第一次发送的seq number +1,以及位码ack是否为1,若正确,客户端会再发送ack number = (服务端的seq + 1),ack = 1,服务端收到后确认seq与ack = 1则连接建立成功,双方状态变为ESTABLISHED

完成三次握手,双方开始通信

四次挥手

1.客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2.服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3.客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4.服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5.客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗ *∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6.服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

为什么握手需要三次,挥手需要四次?

 因为两次握手的话,并不能保证安全问题,就相当于打电话的例子,我给你打过去,证明我的发送功能没有问题,你回过来,说明对端的接收功能和发送功能没有问题,如果这就停止了,并不能证明我的接收功能也是正确的,所以还需要再来一次,这也是为什么不是二次握手,如果再加一次握手,完全没有必要,因为双方的功能都已经被证明OK了,这就是为什么不是四次握手的原因

四次挥手,第一次挥手说明我已经没有数据再要发送了,这时对端先发送确认我已经没有数据要发送了,在对端把剩下的数据都发送过来后,对端再发送我已经没有数据要发送了,可以关闭连接了,这样我收到这个请求后回复确认关闭的应答,就可以进入关闭状态了,所以说挥手需要四次,主要原因是服务端还可能有数据未发送完,需要等一会才能发送FIN,但是我又需要立刻回复对端的FIN已经收到,所以就成了四次挥手。

为什么请求关闭方的TIME_WAIT需要在2MSL之后才能进入CLOSED?

 因为我们不能保证每一条报文对端都能收到,必须要假设最坏条件的发生,如果最后一次ACK服务端没有收到,那么服务端会不断发送FIN包,从而无法关闭,而MSL是报文生存时间,2MSL刚好是数据包在和对端一个来回的时间,当然会较长一点,当等待2MSL后,如果对端没有再发送FIN包,则客户端也进入CLOSED状态,如果收到了FIN包,则时间会重置。

假如主动关闭方发送ACK之后直接关闭连接,如果又重新启动程序,并使用相同的端口以及地址,有可能会收到服务端重发的fin请求,对新连接造成影响(新连接的SYN请求过去后,因为服务端一直在等ACK,因此会认为这个SYN不合法,因此重置连接)

如果已经建立了连接,客户端突然崩溃

 TCP设有一个保活机制,如果客户端突然挂掉,服务端不能让资源一直被占用着,白白浪费,所以说,当服务端每收到一次客户端的请求时,都会将这个时间重置,如果两个小时,两端未发生交流,则服务端会向客户端发送一个保活探测包,如果没有响应,则每隔75s就发送一次,知道发送10个探测包后关闭连接。

如果在三次握手期间崩溃,那么服务端会发送一个RST,重置这个连接,并且释放掉这个socket

 三次握手过程中可以发送数据吗?

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

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

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

什么是半连接队列?

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

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

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

SYN攻击是什么?

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

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

netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:

缩短超时(SYN Timeout)时间
增加最大半连接数
过滤网关防护
SYN cookies技术

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值