一、3次握手、4次挥手的简单描述
1、3次握手
三次握手(Three-way Handshake)指建立一个TCP连接时,需要客户端和服务器总共发送3个包。流程简单描述如下图所示:
在socket编程中,客户端执行connect()时,将触发三次握手。开始时客户端处于 closed 的状态,服务端处于 listen 状态。
(1)第1次握手:客户端给服务端发一个 SYN 报文(SYN=1),随机产生1个初始化序列号seq,(比如seq=x)等待服务器端确认。此时客户端处于 SYN,_SEND 状态。
(2)第2次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=x+1,随机产生一个序号值seq(比如seq=k),并将该数据包发送给客户端以确认连接请求。此时服务器端进入SYN_RCVD状态。
(3)第3次握手:客户端收到 SYN 报文之后,检查ACK是否=1,ack是否=x + 1。然后客户端会发一个ACK包,包的ACK=1,ack=k+1。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态。完成3次握手后,客户端与服务器端之间就可以传输数据了。
2、4次挥手
4次挥手即终止TCP连接,需要客户端和服务端总共发送4个包以确认连接的断开。
(1)第1次挥手:客户端发送一个 FIN 报文(FIN=1),报文中会指定一个序列号seq(比如seq=u)。此时客户端处于 FIN_WAIT1 状态。
此时客户端停止发送数据,主动关闭TCP连接,进入FIN_WAIT1状态,等待服务端的确认。
(2)第2次挥手:服务端收到 FIN 之后,会发送 ACK 报文,ack=u+1,随机产生一个序号值seq=v,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2状态,等待服务端发出的连接释放报文段。
(3)第3次挥手:如果服务端也想断开连接,和客户端的第1次挥手一样,发 FIN 报文,且指定一个序列号seq(比如seq=w)。此时服务端处于 LAST_ACK 的状态。
(4)第4次挥手:客户端收到 FIN 之后,也发送 ACK 报文,ack=w+1,此时客户端处于 TIME_WAIT 状态。客户端需要等2MSL的时间以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于 CLOSED 状态了。
MSL:报文的最长生存时间。那到底2MSL是多长时间呢?
规定是MSL为2分钟,2MSL就是4分钟。但是实际中30秒、1分钟、2分钟都在使用。
二、TCP协议头部格式
网络中传输的数据包由两部分组成:一部分是协议所要用到的首部,另一部分是上一层传过来的数据。首部的结构由协议的具体规范详细定义。
(1)源端口: 占16bit,写入源端口号。
(2)目的端口: 占16bit,写入目的端口号。源端口号和目地端口各占16bit两个字节,也就是端口的范围是2^16=65535。另外1024以下是系统保留的,从1024-65535是用户使用的端口范围。
(3)序号(seq): 占32bit,取值范围[0,2^32-1],一个传输方向上的字节流的每个字节的序号,通过这个来确认发送的数据有序。序号增加到最后一个后,下一个序号就又回到0。
(4)确认号(ack): 占32比特,取值范围[0,2^32-1],对上一次seq序号做出的确认号,用来响应TCP报文段,值是seq+1。若确认号=n,则表明到序号n-1为止的所有数据都已正确接收,期望接收序号为n的数据。
(5)同步标志位SYN: 用于建立会话连接,同步序列号。
(6)确认标志位ACK: 取值为1时确认号字段才有效;取值为0时确认号字段无效。TCP规定,在连接建立后所有传送的TCP报文段都必须把ACK置1。
(7)终止标志位FIN: 用来释放TCP连接。
(8)复位标志位RST: 用来复位TCP连接。
(9)推送标志位PSH: 接收方的TCP收到该标志位为1的报文段会尽快上交给上层应用,而不必等到接收缓存都填满后再向上交付。
(10)数据偏移: 占4比特,并以4字节为单位。用来指出TCP报文段的数据载荷部分的起始处距离TCP报文段的起始处有多远。这个字段实际上是指出了TCP报文段的首部长度。
(11)窗口: 占16比特,以字节为单位。指出发送本报文段的一方的接收窗。
(12)校验和: 占16比特,检查范围包括TCP报文段的首部和数据载荷两部分。在计算校验和时,要在TCP报文段的前面加上12字节的伪首部。
(13)紧急指针: 占16比特,以字节为单位,用来指明紧急数据的长度。
(14)填充:由于选项的长度可变,因此使用填充来 确保报文段首部能被4整除,(因为数据偏移字段,也就是首部长度字段,是以4字节为单位的)。
三、几个知识点
1、为什么需要3次握手?2次不行吗?
一个完整的TCP连接,必须保证:
(1)客户端必须得出结论:服务端的接收、发送能力正常;客户端自己的接收、发送能力正常。
(2)服务端必须得出结论:客户端的接收、发送能力正常;服务器自己的发送、接收能力正常。
第1次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第2次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第3次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
2、什么是半连接队列?
(1)半连接队列: 服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
(2)全连接队列: 就是已经完成三次握手,建立起的连接就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
3、SYN-ACK 重传次数的问题
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s、2s、4s、8s…
4、ISN(Initial Sequence Number)是固定的吗?
ISN就是赋给seq的值。ISN随时间而变化,每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1 。这样做的目的:
(1)防止在网络中被延迟的分组在以后又被传送。
(2)3次握手的其中一个重要功能是客户端和服务端交换 ISN,以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号。
5、3次握手过程中可以携带数据吗?
第1次、第2次握手不可以携带数据。
第3次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据。
6、什么是SYN攻击?
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复ACK包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
7、2MSL等待状态
报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。
8、四次挥手释放连接时,等待2MSL的意义?
为了保证客户端发送的最后一个ACK报文段能够到达服务器。
因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
参考:
(1)深入浅出TCP三次握手 (多图详解)
(2)一文彻底搞懂 TCP三次握手、四次挥手过程及原理
(3)面试官,不要再问我三次握手和四次挥手