文章目录
传输控制协议TCP:Transmission Control Protocol
- 面向连接的、可靠的、基于字节流的传输层通信协议
- 将应用层的数据流分割成报文段并发送给目标结点的TCP层
- 数据包都有序号,对方收到则发送ACK确认,未收到则重传
- 使用校验和来校验数据在传输过程中是否有错误
- SYN:同步序列号,用来发起一个连接
- ACK:确认标识,当ACK=1时确认号才有效
- FIN:关闭连接
- seq:序列号,即数据包本身的序列号
- ack:确认号,对收到数据包的确认,值是等待接受的数据包的序列号
说说TCP的三次握手
“握手”是为了建立连接,在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
1. 第一次握手
建立连接时,客户端发送SYN包到服务端,包含一个随机产生的客户端初始序号seq=x,并进入SYN_SENT状态,等待服务端确认。(其中,标志位SYN=1、ACK=0,表示这是一个TCP连接请求报文;序号seq=x,表明传输数据时的第一个数据字节的序号是x)
2. 第二次握手
服务端收到请求后,必须确认客户端的SYN包。同时自己也发送一个SYN包,也就是要发送给客户端SYN+ACK包,此时服务端进入SYN_RECV状态。(其中确认报文段中,标志位SYN=1、ACK=1,表示这是一个TCP连接响应报文,并包含一个随机产生的服务端初始序号seq=y,以及服务端对客户端初始序号的确认号ack=x+1)
3. 第三次握手
客户端收到服务端的SYN+ACK包,向服务端发送一个ACK确认包,此包发送完毕,待服务端确认后,客户端和服务端进入ESTAB_LISHED(TCP连接成功)状态,完成三次握手,随后客户端与服务端之间就可以开始传输数据了。(其中,标志位ACK=1,并包含序列号seq=x+1,确认号ack=y+1)
简单来说,三次握手的过程就是:
- 客户端向服务端发送连接请求
- 服务端对收到的报文段进行确认
- 客户端再次对服务端的确认进行确认
为什么需要三次握手才能建立连接?而不是两次?不是四次?
首先 ,三次握手的最主要目的是保证TCP连接是双工的:
- 为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次(第一次和第二次)握手
- 为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次(第二次和第三次)握手
其次,采用三次握手是为了防止失效的连接请求报文段突然又传送到服务端,因而产生错误。
-
失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是失效的。
-
如果TCP的握手是两次:客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样就会浪费服务端的连接资源。
-
如果TCP的握手是四次:
- 客户端给服务端发送SYN同步报文
- 服务端收到SYN后,给客户端回复ACK确认报文
- 服务端给客户端发送SYN同步报文
- 客户端给服务端发送ACK确认报文
第2.3步之间,很明显这两步可以合并,从而提高连接的速度和效率。
说说TCP的四次挥手
“挥手”是为了断开连接。断开连接请求可以由客户端发出,也可以由服务端发出,在这里我们称A端向B端请求断开连接。
1. 第一次挥手
如果A认为数据发送完成,则向B发送一个FIN包来关闭A—>B的数据传送。此时,A将进入FIN_WAIT_1状态。(其中报文头中,FIN=1,seq=u;表示该报文段是一个连接释放请求,u-1是A向B发送的最后一个字节的序号)
2. 第二次挥手
B收到请求后,由于B现在可能现在还有数据没有传完,所以B会通知相应的应用程序,告诉它们A—>B这个方向的连接已经释放。此时B进入CLOSE_WAIT状态,并向A发送一个ACK包,即连接释放的应答。(其中,报文头包含 ACK=1,seq=v,ack=u+1;除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答,v-1是B向A发送的最后一个字节的序号,u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节)
A收到该应答,进入FIN_WAIT_2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
3. 第三次挥手
当B向A发完所有数据后,则向A发送一个FIN包来关闭B—>A的数据传送,此时B进入LAST_ACK状态。(其中请求头中,FIN=1,ACK=1,seq=w,ack=u+1)
4. 第四次挥手
A收到释放请求后,向B发送确认应答ACK,此时A进入TIME_WAIT状态。该状态会持续2MSL(Maximum Segment Lifetime,最大报文段生存时间)时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态。当B收到确认应答后,也进入CLOSED状态。
为什么需要四次挥手才能断开连接?
因为TCP是全双工连接,发送方和接收方都需要FIN报文和ACK报文,这样就需要四次了。
为什么A要先进入TIME_WAIT状态,等待2MSL时间后才进入CLOSED状态?
第一,为了保证A发送的最后一个ACK报文能够到达B。这个ACK报文段有可能丢失,因而使处在LAST_ACK状态的B收不到A的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。如果A在TIME_WAIT状态不等待一段时间,而是在发送完ACK报文段后就立即释放连接,就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常的步骤进入CLOSED状态。
第二,A在发送完ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段。
为什么连接的时候是三次握手,关闭的时候却是四次?
在TCP建立连接的过程中,服务端的SYN和ACK向客户端发送是一次性发送的,而在断开连接的过程中,B向A发送的ACK和FIN是分两次发送的。因为在B接收到A的FIN后,B可能还有数据要传输,所以先发送ACK,等B处理完自己的事情后就可以发送FIN断开连接了。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP设有一个保活计时器,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
服务器出现大量CLOSE_WAIT状态的原因?
对方关闭socket连接,我方忙于读或写,没有及时关闭连接
- 检查代码,特别是释放资源的代码
- 检查配置,特别是处理请求的线程配置