TCP是一个面向连接的、可靠的、基于字节流的传输层协议。
TCP连接建立
建立一个TCP连接需要经过三次握手
起初,客户端和服务端都处于CLOSED状态,服务端在监听到某个端口后到了listen状态。
1.客户端发起第一次握手,发起SYN=1,seq为随机数值x的数据包到服务端,客户端变为SYN-SENT状态。
2.第二次握手服务端收到客户端发起的SYN数据包,进入SYN-RCVD状态,并回送SYN=1,ACK=1,ack序列号为第一次握手seq数值x加一,seq为随机数y的数据包。
3.第三次握手客户端收到服务端回送的SYN和ACK数据包,进入ESTABLISHED状态,并进行回送服务端SYN=0,ACK=1,seq为第二次握手ack序列,ack序列为第二次握手seq数值+1的数据包。服务端收到第三次握手数据包后也进入ESTABLISHED状态。
为什么TCP连接不能是二次握手?
假如有一个TCP连接正在建立,客户端发出的某一个SYN数据包并没有丢失,只是在某个网络节点长时间滞留了,这时候TCP进行超时重传,两次握手建立了连接。在连接关闭后的某个时间点,滞留的SYN数据包到了,服务端认为客户端发起请求的SYN数据包,发送ACK数据包并进入SYN-RCVD状态,浪费资源。
只有第三次握手的时候可以携带数据。
如果前两次握手的时候可以携带数据,服务器攻击者想攻击服务器只需要在第一次我收的SYN包里面装上大量数据,就会使服务器消耗大量时间和空间资源,是服务器更容易被攻陷。而第三次握手的时候TCP连接的客户端和服务端都处于稳定的ESTABLISHED状态,确认服务器的发送和接收能力是正常的,可以携带数据。
TCP连接关闭
关闭一个TCP连接需要经过四次握手
客户端和服务端都处于ESTABLISHED状态。
1.第一次握手客户端发起一个FIN数据包请求,seq为随机数值p,发出后客户端变为FIN-WAIT-1状态。
2.第二次握手,服务端收到客户端发送的FIN包后改变为CLOSED-WAIT状态,向客户端发送一个ACK数据包,ack确认号为第一次握手的seq随机数值p加一。
3.客户端收到服务端回送的ACK包之后变为FIN-WAIT2状态,并等待服务器继续发送。
4.第三次握手服务端发送一个FIN数据包,FIN=1,ACK=1,seq为随机数值q,ack确认号为p+1,服务端进入LAST-ACK状态。
5.第四次握手,客户端接收第三次握手后发送ACK包ACK=1,ack确认号为第三次握手随其数值q加一,客户端进入TIME-WAIT状态,等待2MSL后进入CLOSED状态。服务端在收到ACK包后进入CLOSED状态。
为什么TIME-WAIT要等待2MSL?
1.确保第四次握手ACK包能被服务端接收。 2.若不等待2MSL客户端就进入CLOSED,最后一次握手ACK包丢失后服务端就会一直处于LAST_ACK状态,此时服务端重传但客户端处于CLOSED状态无法接收。
为什么是四次握手而不是三次握手?
服务端收到客户端的FIN数据包后不会立刻返回FIN数据包,会等把服务端剩余的报文都发送了才会发送FIN数据包,这段时间内客户端收不到FIN会认为自第一次握手发送的FIN数据包没有送到服务端,会一直重传FIN数据包,因此需要在服务端传一个ACK通知客户端已收到关闭请求但需延迟发送FIN数据包。