目录
①为什么需要三次握手建立连接?为什么不能是两次握手建立连接?
①为什么客户端收到第三个数据报,不能马上置为CLOSED状态?
③客户端收到第三个数据报,状态置为TIME_WAIT后,需要等待多久才能置为CLOSED?
特别注意:连接是有方向的!!!!
一、建立连接:三次握手
目的:确认双方的接收能力和发送能力是否正常,并且指定自己的初始化序列号 为后面的可靠性传输做准备。
本质:双方保存了一个连接状态。(两个ESTABLISHED)
进行三次握手的流程:
刚开始客户端处于CLOSED状态,服务端处于LISTEN状态。
(1)第一次握手:客户端发送SYN报文给服务端,目的:申请建立客户端到服务端的连接。等待服务器确认;
(2)第二次握手:服务端返回服务端的SYN报文和ACK应答报,目的:申请建立服务端到客户端的连接。等待客户端确认;
(3)第三次握手:客户端返回ACK应答报,目的:对第二个数据报SYN的应答,表示已经收到服务端的SYN报文。此时,客户端处于ESTABLISHED状态,服务端收到ACK报文之后也处于ESTABLISHED状态,双方已建立起了连接。
①为什么需要三次握手建立连接?为什么不能是两次握手建立连接?
- 原因(1): 因为只有三次握手才能确认双方的接收和发送功能都是正常的。两次握手只能保证单向连接是畅通的。因为TCP是一个双向传输协议,只有经过第三次握手,才能确保双向都可以接收到对方的发送的数据。
第一次握手:客户端发送报文,服务端收到了。——》对于服务端:客户端的发送能力、服务端的接收能力正常
第二次握手:服务端发送报文,客户端收到了。——》对于客户端:服务端的接收和发送功能是正常的,客户端的接收能力和发送能力正常。但是,服务端无法确认客户端的接收功能是否正常,也就是服务端无法确认客户端是否接收到了。
第三次握手:客户端发送报文,服务端收到了。——》对于服务端:客户端的接收和发送功能正常,服务端的接收和发送功能正常。
因此,需要三次握手才能确认双方的接收能力和发送能力是否正常。
- 原因(2):主要是为了防止已经失效的连接请求报文突然又传送到了服务器,从而导致不必要的错误和资源的浪费。
如果是两次握手的话,客户端发出连接请求,但是因为连接请求报文丢失而未收到确认,由于超时重传机制,客户端会再传一次连接请求,客户端确认于是就建立了连接。传输完毕后,就释放了连接,但是呢,第一个请求并没有丢失,只是因为在网络中滞留了太长时间,后来网络通畅,又到达了服务器,服务端再次确认又建立了连接,但是这个报文本该是失效的,所以导致了不必要的错误和资源的浪费。
如果采用三次握手,那么就算第一次失效的数据报成功传输过来,服务端接收到了那条失效报文,并且回复了确认报文,但是客户端不会再次确认,服务器就不会收到确认,所以服务端就知道客户端没有请求连接。
②什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
假如说已经完成三次握手成功建立起连接的,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
二、断开连接:四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手。这由TCP的半关闭造成的。
TCP的半关闭状态就是:TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。——》半关闭=“藕断丝连”
客户端和服务端都可以发起挥手,假如是客户端发起挥手断开连接。
进行四次挥手的流程:
刚开始客户端和服务端都处于ESTABLISHED,假如说客户端先发起挥手
(1)第一次挥手:客户端发送FIN报文给服务端,目的:申请断开客户端到服务端的连接。发送报文后,此时TCP协议进入半关闭状态,客户端不再向服务端传输数据,主动关闭TCP连接,并且将客户端状态置为FIN_WAIT1
(2)第二次挥手:服务端收到FIN报文后,返回ACK应答报文,目的:表示已经收到客户端的请求。将服务端的状态置为CLOSE_WAIT,然后释放客户端到服务端的连接。客户端收到服务端的确认后,将状态置为FIN_WAIT2状态。
(3)第三次挥手:服务端向客户端发送FIN报文,目的:申请关闭服务端到客户端的连接。此时服务端进入LAST_ACK状态。
(4)第四次挥手:客户端收到FIN之后,返回一个ACK应答报文给服务端,目的:表示收到服务端的请求。在第四个数据包发送之后,客户端 进入TIME_WAIT 状态。客户端需要过一段时间才进入CLOSED状态,等待2MSL,为了保证服务端能收到自己的 ACK 报文。当服务端收到 ACK 报文之后,服务端进入 CLOSED 状态,关闭连接。
①为什么客户端收到第三个数据报,不能马上置为CLOSED状态?
因为第四个数据报ACK的时候,可能出现丢包的问题,表示服务端无法断开连接。服务端就会根据超时重传机制,重新发送第三个数据包FIN。此时,客户端如果是CLOSED就无法接收了。
②服务器上为什么会出现大量CLOSE_WAIT?
一般而言,对于服务器上出现大量的CLOSE_WAIT状态,原因是 服务器没有正确的关闭socket,导致四次挥手没有正确完成,服务端没有调用close()方法。因为执行了close()方法才会发送第三个报文,如果没有执行,就会一直是第二个报文。只需要加上对应的close()方法就可以解决问题。
③客户端收到第三个数据报,状态置为TIME_WAIT后,需要等待多久才能置为CLOSED?
需要2MSL。1个MSL是 单个报文传输的最大时间,需要等待的 就是第四次返回 以及 如果第四次丢包就可能重传数据(重传第三次的数据)
④为什么挥手需要四次?
关闭连接时,客户端向服务端发送FIN时,只表示客户端不再发送数据,但是还能接收数据。服务端收到客户端的FIN报文时,需要先回一个ACK应答报文,来告诉客户端收到了他的FIN报文,而服务端可能还有数据要处理和发送,等到服务端不再发送数据时候,才发送FIN报文给客户端来同意关闭连接的。因此,ACK和FIN不能像建立连接时的候那样一起发送,所以需要四次挥手。
⑤为什么第2、3个数据报没有合并?
第2个数据报,是系统内核返回的(不用程序写代码来发送)
第3个数据报,是程序调用close()方法才发送的。因为服务端在关闭连接前,可能需要做一些其他的工作,需要等到服务端将数据都传输完成,再发送FIN给客户端来请求关闭连接。
可以结合问题④的解析。