手麻了。
TCP三次握手
最开始服务端和客户端均处于closed
状态,然后服务端主动开启LISTEN
准备接收报文。
第一次握手。
客户端将TCP
首部中的SYN
置为1,并且初始化一个随机序列号client_isn
。随后将这个SYN
报文(包含随机序列号)发送给服务端,然后进入SYN-SENT
状态。这次握手告知服务端,客户端想要建立TCP
连接。
第二次握手。
服务端接收到客户端发送的SYN
报文后,将TCP
首部中的ACK
置为1,同时将其中的确认应答号变为cilent_isn+1
,生成ACK
报文。然后初始化一个自己的随机序列号server_isn
,并生成SYN
报文。服务端同时将ACK SYN
两个报文一并发送给客户端,然后进入SYN-REVD
状态。此次握手是告诉客户端服务端知晓了需要建立TCP
连接,请根据返回的信息进行应答正式建立连接。
第三次握手。
客户端接受到服务端发送的两个报文,通过ACK
报文确认确实是自己想要建立连接的那台服务器。然后根据server_isn
生成确认应答号以及ACK
报文,并且将ACK
报文发送给服务端,然后进入ESTABLISHED
状态。服务端接受到ACK
报文后就进入ESTABLISHED
状态。双方都进入这个状态后TCP
连接正式建立完毕,可以开始发送HTTP
请求了。此次握手告诉服务器客户端已经就绪,可以进行正式的连接建立。
灵魂拷问:为啥要三次握手,两次不行吗?
两次握手一般就是放弃最后一个握手,带来的最大的问题就是建立历史连接。比如客户端发送第一次SYN
报文后就宕机了,正好这个发送的报文又遇到了网络堵塞,然后我重启客户端又发送一次SYN
报文。服务端这边旧报文比新报文先抵达了,在两次握手的情况下,服务端每接收一个SYN
报文就要建立一个连接,新旧报文明明是要建立相同的链接,但是却各自建立了相同的连接,造成了资源的浪费。
三次握手就能避免这个问题。还是上面的问题,建设两次SYN
报文的cilent_isn
分别是90和100,客户端发送第二次报文后,期望得到的确认号是101,但是实际会得到91,此时客户端发现确认号有问题就会发送RST
告诉服务端,这个90序列号的连接需要释放,不要建立它的连接。
TCP四次挥手
开始时双方均处于ESTABLISHED
状态,两边都可以发起断开连接的请求。下面以客户端为例介绍。
第一次挥手。
客户端发送FIN
报文到服务端,然后进入FIN_WAIT_1
状态。服务端接受到FIN
报文之后进入close_WAIT
状态。此次挥手告诉服务端,客户端想要断开连接。
第二次挥手。
服务器接收到FIN
报文,生成一个ACK
报文发送给客户端。客户端进入FIN_WAIT2
状态。此次挥手告诉客户端,服务端已经明白了想要断开连接的请求。但是服务端可能还要处理一些数据,或者发送一些数据给客户端,暂时还不要断开连接。
第三次挥手。
服务器在上一步中处理完数据后,发送一个FIN
报文,进入LAST_ACK
状态。此次挥手告诉客户端数据处理完毕,可以正式断开连接了。
第四次挥手。
客户端接受到服务器的FIN
报文,明白可以正式断开连接。客户端发送ACK
报文,然后进入TIME_WAIT
状态,之后会自动关闭。服务端接受到这个ACK
报文之后就知道客户端那边已经确认可以关闭了,于是直接关闭连接。
灵魂拷问2.0:能不能只有三次挥手?
也不是不行,三次挥手就是将第二次和三次的ACK FIN
一起发送。此时表示,客户端申请断开连接的时候,服务端这边已经没有需要处理和发送的数据了,所以这两个报文就可以一起发送了。