三次握手是指建立一个TCP连接时,需要客户端和服务器总共发送三个包,进行三次握手的主要作用就是为了确认双方的接受能力和发送能力是否正常,指定自己的初始化序列号为后面的可靠性传送做准备,实质上其实就是连接服务器指定端口,建立TCP连接,并且同步连接双方的序列号和确认号,交换TCP窗口大小信息
刚开始客户端处于Closed的状态,服务端处于Listen状态
进行三次握手,
第一次握手,
客户端给服务端发一个SYN报文,并指明客户端的初始化序列号是ISN,此时客户端处于SYN_SEND状态
首部的同步位SYN=1,初始序列SEQ=X,SYN=1的报文段不能携带数据,但是要消耗掉一个序列号
第二次握手,
服务器收到客户端的SYN报文后,会已自己的SYN报文作为应答,并且也是指定了自己的初始化序列号ISN,同时会把客户端的ISN+1作为ACK的值,表示自己已经收到了客户端的SYN,此时服务器处于SYN_REVD的状态
在确认报文段中SYN=1,ACK=1,确认号ACK=X+1,初始序号SEQ=Y
第三次握手,
客户端收到SYN报文后,发送一个ACK报文,把服务器的ISN+1作为ACK的值,表示已经收到了服务端的SYN报文,此时客户端处于ESTABLISHED状态,服务器收到ACK报文之后,也处于EDTABLISHED(连接成功)状态,此时双方建立起了联系
确认报文段ACK=1,确认号ACK=Y+1,序列号SEQ=X+1(初始为SEQ=X,第二个报文段所以要 +1),ACK报文段可以携带数据,不携带数据则不消耗序列号
发送第一个 SYN的一端将执行主动打开(active open),接收这个SYN并且发回下一个SYN的另一端执行被动打开(passive open)
进行四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的),这由TCP的半关闭(half-close)造成的,所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力
TCP的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或者服务端均可主动发起挥手动作
刚开始双方都处于established状态,假如是客户端先发起关闭请求,四次挥手的过程如下,
第一次挥手,
客户端发送一个FIN报文,报文中会指定一个序列号,此时客户端处于FIN_WAIT1状态
即发出连接释放报文段(FIN=1,序列号seq=u),并且停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认
第二次挥手,
服务端收到FIN之后,会发送ACK报文,且把客户端的序列号值+1作为ACK报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态
即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序列号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放,客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段
第三次挥手,
如果服务端也想断开连接,和客户端的第一次挥手一样,发给FIN报文,且指定一个序列号,此时服务端处于LAST_ACK状态
即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序列号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认
第四次挥手,
客户端收到FIN之后,一样发送一个ACK报文作为应答,且把服务端的 序列号值+1作为自己ACK报文的序列号值,此时客户端处于TIME_WAIT状态,需要过一阵子以确保服务端收到自己的ACK报文之后才会进入CLOSED状态,服务端收到ACK报文之后,就处于关闭连接了,处于CLOSED状态
即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态,此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态
收到一个FIN只意味着在这一方向上没有数据流动,客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态
扩展
1.为什么连接的时候是三次握手?
首先三次握手的目的是确认双方的接收与发送能力是否正常
第一次握手,
客户端发送网络包,服务端收到了
那么服务端得出结论,客户端的发送能力,服务端的接收能力是正常的
第二次握手,
服务端发包,客户端收到了
那么客户端得出结论,服务端的接收,发送能力,客户端的接收,发送能力均正常,但是此时服务器不能确认客户端的接收能力是否正常
第三次握手,
客户端发包,服务端收到
那么如果是两次握手,会出现如下情况
例如,客户端发出请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求,后来收到确认,建立了连接,数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一直等待客户端发送数据,就会浪费资源
2.什么是半连接队列?
服务器第一次收到客户端的SYN之后,就会处于SYN_RCVD状态,此时双方还没有完全建立其来连接,服务器会把此种状态下 的请求连接放在一个队列里,我们把这种队列称之为半连接队列
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中,如果队列满了,就有可能会出现丢包现象
关于SYN-ACK重传次数的问题
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间未收到客户确认包,进行第二次重传,如果重传次数超过最大重传次数,系统将连接信息从半连接队列中删除
3.ISN(Initial Sequence Number)是固定的吗?
当一端为建立连接而发送它的SYN时,它为连接选择一个初始序列号,ISN随时间而变化,因此每个连接都将具有不同的ISN,ISN可以看作是一个32比特的计数器,每4ms加1,这样选择序列号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释
三次握手的其中一个重要功能是客户端和服务端交换ISN,以便让对方知道接下来接收数据的时候如何按序列号组装数据,如果ISN是固定的,攻击者很容易猜出后续的确认号,因此ISN是动态生成的
4.三次握手过程中可以携带数据吗
其实第三次握手的时候,是可以携带数据的,但是第一次,第二次握手不可以携带数据
例如,第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的SYN报文中放入大量的数据,因为攻击者根本就不理服务器的接收,发送能力是否正常,然后疯狂重复发送SYN报文的话,这会让服务器花费很多时间,内存空间来接收这些报文
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器容易受到攻击,而对于第三次的话,此时客户端已经处于ESTABLISHED状态,对于客户端来说,他已经建立起连接,并且也已经知道服务器的接收,发送能力是正常的,所以可以携带数据
5.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_RHCV
常见的防御SYN攻击的方法有
缩短超时(SYN Timeout)时间
增加最大半连接数
过滤网关防护
SYN cookies技术
6.挥手为什么需要四次?
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文,其中ACK报文是用来应答的,SYN报文是用来同步的,但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”,只有等到我i服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,因此需要四次挥手
2MSL等待状态,
TIME_WAIT状态也成为2MSL等待状态,每个具体TCP实现必须选择一个报文端=段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间,这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段
对一个具体实现所给定的MSL值,处理的原则是,当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可以让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用,这个连接只能在2MSL结束后才能再被使用
7.四次挥手释放连接时,等待2MSL的意义?
MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃
为了保证客户端发送的最后一个ACK报文段能够到达服务器,因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文,服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器,最后客户端和服务端都能正常的关闭,假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失,服务器就无法正常的进入关闭连接状态
理由,
保证客户端 发送的最后一个ACK报文段能够到达服务端
这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对方已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段立即释放连接,则无法收到服务端重传FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态
防止已失效的连接请求报文段出现在本连接中
客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段
8.为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSED状态
理论上,四个报文都发送完毕,就可以直接进入CLOSED状态,但是可能网络是不可靠的,有可能最后一个ACK丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文
PS(参考百度百科及其他资料)