TCP 建立连接为什么要握 3 次手?
2019-08-21

上次已经说过,没有协议,不成方圆,计算机之间的通信更是依赖于协议。今天就重点分析一下 TCP 协议。

 

传输控制协议 TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络 OSI 模型中,它完成第四层传输层所指定的功能,用户数据包协议(UDP)是同一层内另一个重要的传输协议。

 

先来复习一下 OSI 的七层模型。

 

 

TCP 工作在 OSI 中的第四层——Transport 层,IP 在第三层——Network 层,ARP 在第二层——Data Link 层;在第二层上的数据,我们把它叫 Frame,在第三层上的数据叫 Packet,第四层的数据叫 Segment。 同时,我们需要知道,数据从应用层发下来,会在每一层都会加上头部信息,进行封装,然后再发送到数据接收端。

 

 

TCP 协议所涉及到的知识太多,没有办法兼顾,我主要想谈谈 3 次握手和 4 次挥手。TCP 的运行可以分为三个阶段,建立连接、传送数据、关闭连接。3 次握手 4 次挥手就对应着建立连接和关闭连接。

 

操作系统将 TCP 连接抽象为套接字表示的本地端点,作为编程接口给程序使用。在 TCP 连接的生命期内,本地端点要经历一系列的状态改变。我们经常会听到面向 Socket 编程,这是后话了。

 

要想知道握手是怎样进行了,我们先来看看 TCP 的报文结构。

 

 

 

有以下几点需要说明

 

TCP 报文中没有 IP 地址,有源端口和目的端口,IP 地址在网络层中的 Packet 中。

 

Sequence Number(序号),用来标识从 TCP 发送端向 TCP 接收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据流中的序号;通信双方要知道对方的初始化序号,这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP 会用这个序号来拼接数据)。

 

Acknowledgment Number(确认号)32 位确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加 1。不过,只有当标志位中的 ACK 标志(下面介绍)为 1 时该确认序列号的字段才有效。主要用来解决不丢包的问题。

 

SYN,表示同步序号,用来建立连接。SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1。

 

这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有 SYN 的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行 TCP 三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行 TCP 的三次握手。

 

ACK,TCP 协议规定,只有 ACK=1 时有效,也规定连接建立后所有发送的报文的 ACK 必须为 1 。

 

FIN,表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送 FIN 标志位的 TCP 数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。

 

三次握手的过程,即建立一个 TCP 连接的过程,在 Socket 编程中,这一过程由客户端执行 connect 来触发,整个流程如上图所示。

 

第一次握手:Client 将标志位 SYN 置为1,随机产生一个值 seq=x,并将该数据包发送给 Server,Client 进入 SYN_SENT 状态,等待 Server 确认。

 

第二次握手:Server 收到数据包后由标志位 SYN=1 知道 Client 请求建立连接,Server 将标志位 SYN 和 ACK 都置为1,ack=x+1,随机产生一个值 seq=y,并将该数据包发送给 Client 以确认连接请求,Server 进入 SYN_RCVD 状态。

 

第三次握手:Client 收到确认后,检查 ack 是否为 x+1,ACK 是否为1,如果正确则将标志位 ACK 置为1,ack=y+1,并将该数据包发送给 Server,Server 检查 ack 是否为 y+1,ACK 是否为1,如果正确则连接建立成功,Client 和 Server 进入 ESTABLISHED 状态,完成三次握手,随后 Client 与 Server 之间可以开始传输数据了。

 

注意,不要搞混了 SYN、ACK 只是标志位,seq 是序号,防止乱序,ack 是确认号,主要用来解决不丢包的问题。

 

四次挥手,即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。

 

由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN 来终止这一方向的连接,收到一个 FIN 只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP 连接上仍然能够发送数据,直到对方也发送了 FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

 

第一次挥手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number 和 Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

 

第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

 

第三次挥手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

 

第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明 Server 端已正常关闭,那好,主机1也可以关闭连接了。

 

为什么建立连接需要 3 次握手 ?

因为,TCP 协议要保证两端数据的可靠传输,最少就要采取 3 次交互。三次是保证双方互相明确对方能收能发的最低值。下面有个生活中的例子可以感受一下。

 

TCP之所以使用三次握手,完全是一种为了解决“两军问题”所采用的折衷的设计。所谓“两军问题”,就是红军想告诉蓝军明天下午一起对敌开火,那么红军会派信使 1 号跑过去告诉蓝军,蓝军收到消息再派信使 2 号告诉红军收到,注意,这时蓝军并不知道红军是否收到蓝军的回复。因此需要红军收到回复再派信使 3 号告诉蓝军收到回复,而此时红军也不知道蓝军是否收到回复,因此蓝军收到信使 3 号的消息再派信使 4 号…

 

还有一个秒懂例子,看到这个真的笑死我了。但是这个例子不严谨哦,权当一乐就好。

 

三次握手:“喂,你听得到吗?”“我听得到呀,你听得到我吗?”“我能听到你,今天balabala……”

 

两次握手:“喂,你听得到吗?”“我听得到呀”“喂喂,你听得到吗?”“草,我听得到呀!!!!”“你TM能不能听到我讲话啊!!喂!”“……”

 

四次握手:“喂,你听得到吗?”“我听得到呀,你听得到我吗?”“我能听到你,你能听到我吗?”“……不想跟傻逼说话”

 

所以说,握手次数要保持刚刚好,2 次不够,4 次多了。

 

SYN 攻击

SYN 攻击指的是,攻击客户端在短时间内伪造大量不存在的 IP 地址,向服务器不断地发送 SYN 包,服务器回复确认包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

 

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。

 

如何防御 SYN 攻击 ?

SYN 攻击不能完全被阻止,除非将 TCP 协议重新设计。我们所做的是尽可能的减轻 SYN 攻击的危害,常见的防御 SYN 攻击的方法有如下几种:

 

缩短超时(SYN Timeout)时间

增加最大半连接数

过滤网关防护

SYN cookies 技术