在正常的情况下,TCP要经过三次握手建立连接,四次挥手断开连接;
演示建立和关闭TCP连接。并通过TCP建立连接和终止连接的时序图以及报文分析。
三次握手:
这里将通信双方分别称为发送端(客户端)和接收端(服务器端)。三次握手是应用于发送端和接收端在数据交互前建立TCP连接的过程,这个过程需要三个步骤才能完成:
服务器状态转化:
[CLOSED -> LISTEN]:服务器端调用listen状态,等待客户端连接;
-
第一次握手:
发送端为即将建立的连接生成一个TCP报文段中的序号,并将SYN置为1,(初始序号seq=x)发送SYN包至接收方,然后进入SYN_SENT状态;
[LISTEN -> SYN_RCVD]:一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文。 -
第二次握手:
接收端在收到SYN包后,进入SYN_RCVD状态,需要确认客户的SYN接收到,则将接受到的(序号+1)作为返回报文的确认序号;【确认号ack=x+1,初始序号seq=y】
同时自己也发送一个SYN包(syn=y),即SYN+ACK包,且SYN和ACK都置为1;
该包有两个作用:作为SYN包进行建立连接,同时作为ACK包回复发送端。
[SYN_RCVD -> ESTABLISHED]:服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据; -
第三次握手:
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据。
【确认号ack=y+1,序号seq=x+1)(初始为seq=x,第二个报文段所以要+1)】
因为 建立连接的过程需要三次交互通信,所以称为三次握手。
### 四次挥手:
在数据交互完成后,需要关闭连接释放系统资源,如:内存、文件等;因为TCP连接是全双工模式,需要关闭双向的数据发送。
这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
为了保证释放资源的可靠性,需要四次挥手。四次挥手过程中,发送端和接收端都要关闭各自连接释放资源,从而避免半关闭状态。
【A:发送端;B:接收端 A、B连接建立状态ESTABLISHED -> A终止,等待1状态FIN-WAIT-1 -> B关闭,等待状态CLOSE-WAIT -> A终止,等待2状态FIN-WAIT-2 -> B最后确认状态LAST-ACK -> A时间等待状态TIME-WAIT -> B、A关闭状态CLOSED】
四次挥手过程:
- 发送端先向其TCP发出 连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1(终止等待1)状态,等待接收端的确认。
- 接收端收到 连接释放报文段后,即发出确认报文段,(ACK=1,确认号ack=u+1,序号seq=v),接收端进入CLOSE-WAIT(关闭等待)状态,此时的TCP处于半关闭状态,接收端到发送端的连接释放。
- 发送端收到接收端的确认后,进入FIN-WAIT-2(终止等待2)状态,等待接收端发出的 连接释放报文段。
- 接受端没有要向发送端发出数据,接受端发出 连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1 两次一样),接受端进入LAST-ACK(最后确认)状态,等待发送端的确认。
- 发送端收到接受端的 连接释放报文段,对此发出确认报文段(ACK=1,序号seq=u+1,确认号ack=w+1),发送端进入TIME-WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,发送端才进入CLOSED状态。
状态详解:
CLOSED:表示初始状态;
LISTEN:表示服务器端的某个SOCKET处于监听状态,可以接受连接。
SYN_RCVD:这个状态表示接受了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,(短暂),这种状态时,当收到客户端的ACK报文会进入到ESTABLISHED状态;
SYN_SENT:当客户端SOCKET执行连接时,首先发送SYN报文,随后进入SYN_SENT状态,并等待服务端的发送三次握手中的第二个报文。SYN_SENT状态标识客户端已发送SYN报文。
ESTABLISHED:连接已经建立;
FIN_WAIT_1:FIN_WAIT_1与FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而二者的区别是:FIN_WAIT_1状态实际是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET进入FIN_WAIT_1状态。
而当对方回应ACK报文后,则进入FIN_WAIT_2状态;在实际正常情况下,无论对方在何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1一般很难见到;而FIN_WAIT_2状态有时可以用netstat看到;
FIN_WAIT_2:FIN_WAIT_2状态下的SOCKET,表示半连接;即 一方要求关闭连接,但另外需告诉对方,我暂时还有数据需要传送给你,稍后再关闭连接;
TIME_WAIT:表示收到了对方的FIN报文,并发送了ACK报文,再等2MSL后即可回到CLOSED状态;如果FIN_WAIT_1状态下,收到了对方的同时带FIN标志和ACK标志的报文时,可以直接进入TIME_WAIT状态;
CLOSING:比较特殊;正常情况下,当你发送FIN报文后,按理应先收到对方的ACK报文再收到FIN报文;但CLOSING状态表示你发送了FIN报文后,没有收到对方的ACK报文,却收到对方的FIN报文;这时是因为双方同时关闭一个SOCKET;
CLOSE_WAIT:表示在等待关闭;在收到对方发来的FIN报文,回复一个ACK报文给对方,进入该状态;在该状态下,需要完成的是:等待你去关闭连接;
LAST_ACK:它是指 被动关闭的一方在发送了FIN报文后,最后等待对方的ACK报文,当收到ACK报文后则进入CLOSED状态;
MSL在RFC1122中规定为两分钟,但是各操作系统的实现方式不同,在Centos7上默认配置的值是60s;
总结:
- 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接受数据,而自身未必把所有数据都发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的.
- 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
因为这里虽然双方都同意关闭连接了,而且挥手的4个报文也都协调发送完毕,按理可以直接回到CLOSED状态,但是因为我们必须想到网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方接受,因此对方处于LAST-ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,这时主动关闭方的TCP连接已经关闭,在接收到这个FIN包后寻找不到对应的连接,从而回复RST复位包,被动关闭方接收到RST包后会处理错误从而导致无法正常关闭连接释放资源。
所以这个TIME-WAIT状态的作用是用来重发可能丢失的ACK报文。
3.TIME_WAIT的作用:
- 保证最后的ACK报文被接受;主动关闭连接的一方发送最后一个ACK报文,进入TIME_WAIT状态,在等待期间不收到服务器端发送的FIN(结束报文段),则表示此时发送的ACK报文成功发送;若收到FIN报文,则再重新发送ACK报文;
- TIME_WAIT;在该段时间内,保证数据消散开。
- 为什么TIME_WAIT的时间是2MSL?
MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话;
- 可以保证在两个传输方向上的尚未被接受或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据可能是错误的);
【例: 因为TCP连接时全双工的双向通信,关闭时需要双端都关闭保证双向都没有数据流通。
当主动方发起关闭,只能保证主动方将没有任何数据向外发送。此时被动仍然可能会发送报文,加入这些报文在网络节点中停留,然后主动方又立即与被动方建立新的TCP连接且新的连接和上次TCP连接的标识是一样的,此时在网络中停留的报文也刚好抵达主动方,主动方处理该报文必然会发生错误。
主动关闭方在停留2MSL的TIME_WAIT状态,保证TCP连接中网络报文尽可能的过期。】
- 同时也是在理论上保证了最后一个报文安全到达(假设最后一个ACK报文丢失,那么服务器会重新发送一个FIN报文,这时虽然客户端的进程不在了,但是TCP的连接还在,仍然可以重发LAST_ACK);由于ACK包和FIN包都在网络中停留最长时间为MSL,所以TIME_WAIT为2MSL,保证尽可能最长的时间接受被动关闭方的FIN包
- 二次握手的缺点与三次握手的优点?
两次握手的缺点:最新消息(服务器端发送的ACK+SYN报文)丢失后,客户端认为没成功建立链接,而服务器认为成功建立链接,影响服务器;
3次握手的优点:第3次应答(客户端最后发送的ACK报文)丢失后,影响的是客户端;