一、三次握手
意义
TCP三次握手其实就是建立一个TCP连接,客户端与服务端交互需要三个数据包,握手的主要作用就是为了确认双方的接收和发送能力是否正常、初始序列号、交换窗口大小等信息。
过程
实际上这个过程可以用A和B打电话来比喻
A:喂,能听到吗
B:可以,那你能听到我说话吗
A:没问题啊
然后开始唠嗑......
一次握手:客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入SYN_SEND 状态,等待服务器的确认;
第一次握手证明客户端的发送能力正常。
二次握手:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 SYN_RECV 状态。
第二次证明服务器端的接收能力、发送能力正常。
三次握手:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务器端都进入ESTABLISHED 状态,完成 TCP 三次握手。
第三次证明客户端的接收能力正常。
那么问题来了,两次握手可以吗?
1.从确认双方收发能力来看
显然是不可以的,如果只有两次握手,那并不能证明客户端的接收能力是正常的。只有三次握手才能证明双方的发送和接收能力是正常的。
2. 从序列号可靠同步来看
如果是两次握手,服务端无法确认客户端是否已经接收到了自己发送的初始序列号,如果第二次捂手报文丢失,那么客户端就无法知道服务端的初始序列号,那TCP的可靠性就无从谈起了。
3.从阻止重复历史连接的初始化来看
客户端由于某种原因发送了两个不同序列的SYN包,而网络环境通常是复杂的,旧的数据包有可能先到达服务器,如果是两次握手,服务器收到旧的SYN就会立刻建立连接,那么就会造成网路的异常。如果是三次握手,服务器需要回复SYN+ACK包,客户端对比应答序号,如果发现是旧的报文,就会给服务器发送RST报文,直到正常的SYN到达服务器后才正常建立连接。所以,只有三次握手才有足够的上下文信息来判断当前连接是否是历史连接。
4.安全问题
我们直到TCP新建连接时,内核会为连接分配一系列的内存资源,如果采用两次握手就建立连接,那么会放大DDOS的攻击。TCP作为一种可靠的传输控制协议,核心思想是既要保证数据可靠传输,又要提高传输的效率,三次握手可以满足以上两方面的要求。
三次握手可以携带数据吗?
第一次和第二次握手不可以携带数据,第三次握手可以。
假如第一次握手可以携带数据,如果有人恶意攻击服务器,那他每次都在第一次握手SYN报文中放入大量的数据,疯狂重复发SYN报文,这会让服务器花费大量的内存空间来缓存这些报文,这样的话服务器就更容易被攻击了。对于第三次握手,此时客户端已经处于连接状态,他已经知道服务器的接收发送能力是正常的了,所以可以携带数据是情理之中的事情。
二、四次挥手
断开一个 TCP 连接则需要“四次挥手”,缺一不可:比如客户端主动发起断开请求
第一次挥手:客户端发送一个 FIN(SEQ=x) 标志的数据包->服务端,用来关闭客户端到服务器的数据传送。然后客户端进入 终止等待1(FIN-WAIT-1) 状态。
第二次挥手:服务器收到这个 FIN(SEQ=X) 标志的数据包,它发送一个 ACK (ACK=x+1)标志的数据包->客户端 。然后服务端进入 关闭等待(CLOSE-WAIT) 状态,客户端进入 终止等待2(FIN-WAIT-2) 状态。
第三次挥手:服务端发送一个 FIN (SEQ=y)标志的数据包->客户端,请求关闭连接,然后服务端进入 最后确认(LAST-ACK) 状态。
第四次挥手:客户端发送 ACK (ACK=y+1)标志的数据包->服务端,然后客户端进入超等等待(TIME-WAIT)状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 关闭(CLOSE) 状态。此时如果客户端等待 一段时间(2MSL) 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。
为什么要四次挥手?
TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
举个例子:A 和 B 打电话,通话即将结束后。
- 第一次挥手:A 说“我没啥要说的了”
- 第二次挥手:B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
- 第三次挥手:于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
- 第四次挥手:A 回答“知道了”,这样通话才算结束。
为什么第四次挥手客户端需要等待 2*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?
客户端需要等待超时时间,这是为了保证对方已经收到ACK包,如果客户端发完ACK包就关闭,一旦ack包丢失,服务端将一直停留在最后确认状态。如果客户端可以等待一段时间,这时服务端因为没有收到ack包,会重发fin包,客户端会响应这个fin包,重发ack包并且刷新超时时间,这就可以保证进行可靠的连接断开。