TCP握手

TCP连接与断开

Posted by Bug1024 on February 1, 2017

连接建立过程

TCP 连接的建立采用客户服务器方式,主动发起连接建立的一方叫客户端(Client),被动等待连接建立的一方叫服务器(Server)

最初的时候,两端都处于 CLOSED 的状态,然后服务器打开了 TCP 服务,进入 LISTEN 状态,监听特定端口,等待客户端的 TCP 请求。

第一次握手: 客户端主动打开连接,发送 TCP 报文,进行第一次握手,然后进入 SYN_SEND 状态,等待服务器发回确认报文。 这时首部的同步位 SYN = 1,同时初始化一个序号 Sequence Number = J。 TCP 规定,SYN 报文段不能携带数据,但会消耗一个序号。

第二次握手: 服务器收到了 SYN 报文,如果同意建立连接,则向客户端发送一个确认报文,然后服务器进入 SYN_RCVD 状态。 这时首部的 SYN = 1,ACK = 1,而确认号 Acknowledgemt Number = J + 1,同时也为自己初始化一个序号 Sequence Number = K。 这个报文同样不携带数据。

第三次握手: 客户端收到了服务器发过来的确认报文,还要向服务器给出确认,然后进入 ESTABLISHED 状态。 这时首部的 SYN 不再置为 1,而 ACK = 1,确认号 Acknowledgemt Number = K + 1,序号 Sequence Number = J + 1。 第三次握手,一般会携带真正需要传输的数据,当服务器收到该数据报文的时候,就会同样进入 ESTABLISHED 状态。 此时,TCP 连接已经建立。

对于建立连接的三次握手,主要目的是初始化序号 Sequence Number,并且通信的双方都需要告知对方自己的初始化序号,所以这个过程也叫 SYN。 这个序号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输问题而乱序,因为TCP 会用这个序号来拼接数据。

TCP 泛洪攻击

知道了 TCP 建立一个连接,需要进行三次握手。 但如果你开始思考「三次握手的必要性」的时候,就会知道,其实网络是很复杂的,一个信息在途中丢失的可能性是有的。 如果数据丢失了,那么,就需要重新发送,这时候就要知道数据是否真的送达了。 这就是三次握手的必要性。 但是再向深一层思考,你给我发信息,我收到了,我回复,因为我是君子。 如果是小人,你给我发信息,我就算收到了,我也不回复,你就一直等我着我的回复。 那么很多小人都这样做,你就要一直记住你在等待着小人1号、小人2号、小人3号……直到你的脑容量爆棚,烧坏脑袋。 黑客就是利用这样的设计缺陷,实施 TCP Flood 攻击,属于 DDOS 攻击的一种。 想了解更多 SYN Flood 攻击请看:SYN flood - wiki

四次挥手,释放连接

TCP 有一个特别的概念叫做半关闭,这个概念是说,TCP 的连接是全双工(可以同时发送和接收)的连接,因此在关闭连接的时候,必须关闭传送和接收两个方向上的连接。 客户端给服务器发送一个携带 FIN 的 TCP 结束报文段,然后服务器返回给客户端一个 确认报文段,同时发送一个 结束报文段,当客户端回复一个 确认报文段 之后,连接就结束了。

释放连接过程

在结束之前,通信双方都是处于 ESTABLISHED 状态,然后其中一方主动断开连接。 下面假如客户端先主动断开连接。

第一次挥手: 客户端向服务器发送结束报文段,然后进入 FIN_WAIT_1 状态。 此报文段 FIN = 1, Sequence Number = M。

第二次挥手: 服务端收到客户端的结束报文段,然后发送确认报文段,进入 CLOSE_WAIT 状态。 此报文段 ACK = 1, Sequence Number = M + 1。

客户端收到该报文,会进入 FIN_WAIT_2 状态。

第三次挥手: 同时服务端向客户端发送结束报文段,然后进入 LAST_ACK 状态。 此报文段 FIN = 1,Sequence Number = N。

第四次挥手: 客户端收到服务端的结束报文段,然后发送确认报文段,进入 TIME_WAIT 状态,经过 2MSL 之后,自动进入 CLOSED 状态。 此报文段 ACK = 1, Sequence Number = N + 1。

服务端收到该报文之后,进入 CLOSED 状态。

关于 TIME_WAIT 过渡到 CLOSED 状态说明: 从 TIME_WAIT 进入 CLOSED 需要经过 2MSL,其中 MSL 就叫做 最长报文段寿命(Maxinum Segment Lifetime),根据 RFC 793 建议该值这是为 2 分钟,也就是说需要经过 4 分钟,才进入 CLOSED 状态。

为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。