TCP为什么一定需要三次握手?偶然又或者是有原因的?

                   TCP为什么需要三次握手?

目录

         TCP为什么需要三次握手?

什么是TCP协议

TCP的表头含义

TCP的“三次握手”连接过程

为什么刚好是三次握手?两次握手不可以吗?

SEQ的初始值为什么是随机的?

抓取真实的TCP包

博客文章版权声明


 

 

在我们探讨TCP为啥一定要握3次手之前,先让我们从TCP协议的基础开始研究,TCP协议到底是个什么东西。

    

什么是TCP协议


TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。还有另一种UDP协议,也是和TCP协议处于同一层的。在因特网的协议族中,TCP位于IP层之上,应用层之下。不同的主机之间需要可靠的,像管道一样的连接用来交换数据信息,然而,IP协议却并不提供这样的功能,并且IP协议提供的是不可靠的包交换。

处于网络体系的最上层的应用层,如果需要发送数据,应用层会向下面的TCP层发送用8位字节表示的数据流。接下里,TCP收到数据流后,会把数据流分割成适当长度的报文段(大小通常即为该计算机的数据链路层的最大传输单元MTU大小)。TCP完成了自己的本职工作后,会把分割报文在传给它的下一层IP层,由它负责把报文通过网络传输给接收端的TCP层。TCP为了弥补IP层无法保证“可靠性”的缺点,给每一个包都打上一个序号,同时该序号也保证了接收端能够按照正确的顺利接收发来的包。接受端收到包后,会给发送端发送一个相应的“确认”,我们把它叫做ACK。但是,如果发送端在合理的往返时延(RTT)内未收到确认,那么发送端就会认为自己发送的包在传输过程中被丢失了,接下来它就会进行重传。为了保证TCP数据包不在传输过程中被“篡改”,TCP用一个校验和函数来检验数据在传输的过程中是否发生了改变,因此,在发送时和接收时都要进行校验和的计算。这些手段,使得TCP具有了一定的“可靠性”。

TCP协议的运行可划分为3个阶段:创建连接、数据传输、终止连接。

 

TCP的表头含义


TCP用一种叫做“三次握手”的方式来创建一个连接。在创建连接的过程中,很多参数会被初始化,例如序号被初始化用来保证包的按序传输以及安全性。

一对终端同时初始化一个他们之间的连接是可能的。但是,通常是由一端打开“套接字”然后监听另一方的连接,这就是通常所指的“被动打开”,服务器端被动打开以后,用户端就能够开始创建“主动打开”了。

下面这张图描述了TCP的三次连接过程:

有些同学这么一看,已经蒙圈了,这些字母都是些啥,因此,为了了解一下TCP建立连接的过程,我们首先需要知道TCP报文表头的结构特点是怎样的:

我们可以看到,每一行实际上是4个字节,32位。那么,我们接下来就来了解一下各个字段代表的含义吧:

  • 来源通信端口(16位长)-TCP报文的发送方的端口号
  • 目的通信端口(16位长)-TCP报文的接受方的端口号
  • 序列号(seq,32位长),序列号用来标识从TCP的发送端发往接收端的数据字节流的。TCP用序号对每个字节进行计数,而序列号就表示在这个报文段中的第一个数据字节的序号。但是,请记住!序列号并不是从0,也不是从1开始标记的,而是从一个随机数开始标记,之所以为什么要从一个随机数开始,后面会详细的来讨论这个问题。
  • 确认序号是指发送确认的一端所期望收到的下一个序号。例如,发送端的确认序号如果为X,则表示希望接收端下一次发送以X+1为开头序号的数据字节过来,但是,只有ACK表示设置为1时确认序号的字段才生效哦!
  • 数据偏移也可以认为是TCP的首部长度,占4位。为什么要存在一个数据偏移字段呢?因为我们发现,在红色的选项字段中,该字段的长度是可变的,因此,数据偏移字段就派上用场了,它指示报文中前多少位为TCP表头,但是我们发现,4位最大表示的值为15,而这里TCP的首部最小也有20个字节啊!因此,数据偏移中的每个单位表示的值不是1个字节,而是4个字节,32位。因为数据偏移的最大值为【1111】=15,因此TCP的表头的最大值为15*4字节=60字节,即TCP最多有60字节的首部。如果没有红色的选项字段,则TCP表头长度为20字节,数据偏移的值为【0101】。
  • URG:紧急指针,被设置为1的话表示该TCP报文为高优先级数据包,此时紧急指针字段生效。
  • ACK:确认序号,被设置1表示确认序号字段有效。
  • PSH:为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
  • RST:为1表示出现严重差错。可能需要重新创建TCP连接。还可以用于拒绝非法的报文段和拒绝连接请求。
  • SYN:为1表示这是连接请求或者是连接接受请求,用于创建连接和使顺序号同步。
  • FIN:为1表示发送方没有数据要传输了,要求释放连接。
  • 窗口大小(16位长):TCP的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,表示从确认号开始,本报文的接受方可以接收的字节数,即接收窗口大小。用于流量控制。因为该字段占16位,因此窗口最大可设置为65535字节。
  • 校验和:对整个的TCP报文段,包括TCP头部和TCP数据,以16位字进行计算所得。这是一个强制性的字段。一定是由发送端计算和存储,接收端进行验证。
  • 紧急指针:是一个偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。只有当URG被设置为1时才生效。
  • 选项:最多40字节。每个选项的开始是1字节的kind字段,说明选项的类型。

0:选项表结束(1字节)

1:无操作(1字节)用于选项字段之间的字边界对齐。

2:最大报文段长度(4字节,Maximum Segment Size,MSS)通常在创建连接而设置SYN标志的数据包中指明这个选项,指明本端所能接收的最大长度的报文段。通常将MSS设置为(MTU-40)字节,携带TCP报文段的IP数据报的长度就不会超过MTU,从而避免本机发生IP分片。只能出现在同步报文段中,否则将被忽略。

3:窗口扩大因子(4字节,wscale),取值0-14。用来把TCP的窗口的值左移的位数,使窗口值乘倍。只能出现在同步报文段中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节。

4:sackOK—发送端支持并同意使用SACK选项。

5:SACK实际工作的选项。

8:时间戳(10字节,TCP Timestamps Option,TSopt)

发送端的时间戳(Timestamp Value field,TSval,4字节)

时间戳回显应答(Timestamp Echo Reply field,TSecr,4字节)

数据:TCP报文段中的数据部分是可选的。比如在连接建立和连接终止时,双方交换的报文段仅仅有TCP的首部。一方即使没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。

 

TCP的“三次握手”连接过程


为了建立一条TCP连接,需要以下三个步骤:

1)客户端为了主动打开连接,因此需要发送一个报文给服务端,它设置该报文的SYN字段为1,表示这是一个同步请求报文。并且,客户端把报文的序列号码(SEQ)设置为一个随机值X(一定要随机值!!!)。发送这个报文出去,客户端处于SYN_SENT状态。

2)服务端启动后首先进入LISTEN状态,当它收到客户端发来的报文,发现这是一个SYN同步请求,因此,服务端进入SYN_REV状态,并决定也发送一个报文回复一下客户端。他把报文的SYN字段也设置为1,还把ACK字段也设置为1,也生成一个随机数Y(一定是随机数!!!)作为自己的序列号吗(SEQ),确认号码设置为客户端发来的报文的SEQ的字段序列号+1,即X+1。为什么要加一呢?因为服务端希望收到客户端的下一个字节的序号就是X+1。

3)最后,客户端进入ESTABLISHED状态,表示本方连接已经成功建立,为了回复服务端,他把报文的ACK设置为1,序列号码(SEQ)设置为服务端希望的“X+1”,确认号码设置为Y+1。当服务端接受到这个报文后,服务端也进入ESTABLISHED状态。

 

为什么刚好是三次握手?两次握手不可以吗?


肯定有爱钻牛角尖的同学要问了:为什么恰好3次握手就可以建立连接呢?难道2次握手就不可以连接成功了吗?

对于这个问题,我们首先需要抓住关键点:TCP保证数据包的安全性以及顺序性。

我们知道,网络环境错综复杂,极有可能出现丢包或者延迟的现象,如果第1个数据包在网络中发生了拥堵现象,而第2个数据包却顺利的提前到达了接收端,那么接受端如果按照先后到达的顺序来收数据包的话,收到的数据包顺序很容易就是乱的,根本无法保证可靠性。

因此,TCP协议必须解决顺序错乱这个问题,那么TCP是怎么解决的呢?

在TCP中,双方通过SEQ(序列号)来记录自己发送的报文段中的第一个数据字节序列号。而对于接收者来说,接收者可以利用对方发过来的SEQ进行回应,比如把确认号码设置为对方的SEQ+1,表示我已经收到了你的第SEQ号的包了,我希望你下次再发你的第SEQ+1号的包给我,谢谢啦!

因此,双方通过SEQ,就能够保证每个包即使没有按照先后顺序到达目标,目标也能够通过这些包各自的SEQ把它们用正确的顺序排列好。

那么,我们发现:TCP中的关键点就在于SEQ,如果对于双方来说,都能满足下面两个条件:

1)我知道对方的SEQ。

2)我确认对方已经知道了我的SEQ。

此时,双方就不会发生数据包顺序乱掉的问题了,因为大家收到的包中都含有SEQ值,只要利用SEQ值就能够还原出正确的顺序了。

假设,我们只进行两次握手看看:

 

此时,我们再来看看双方是否都满足上面的两个条件呢?

对于客户端来说,他收到了服务端的ACK确认,即表明服务端已经知道了客户端的SEQ,还收到了服务端发来的服务端SEQ,此时,客户端就满足了:

1)知道了服务端的SEQ。

2)确认服务端知道了自己的SEQ。

此时,对于客户端来说,他其实是已经完成了要求。

我们再来看看服务端这边,服务端收到了客户端发来的客户端SEQ,但是!他并没有收到客户端发来的ACK确认,也就是说:服务端并不知道客户端是否真的收到了服务端发送过去的服务端SEQ,那么,服务端只满足了:

1)知道了客户端的SEQ。

2)不能够确认客户端知道了自己的SEQ。

因此,对于服务端来说,两个条件只满足了其中的一个,他无法确认自己的SEQ是不是在传输过程中丢失了亦或是顺利到达了,这样,客户端可能不知道服务端的SEQ,服务端就无法保证客户端是按照正确的顺序来接受数据包了。

所以我们说,仅仅靠两次握手,是无法让TCP解决“顺序错乱”这个问题的。

那么,如果客户端再向服务端发送一次ACK确认,告诉服务端,我已经成功地收到了你的SEQ,你现在可以放心的开始传输数据了。

此时,服务端就解决了下面这个问题:

2)不能够确认客户端知道了自己的SEQ。

 

因此,我们可以看到,最少需要三次的“握手”,双方才能都满足上述的“两个条件”。

那么,想得深一点的同学可能又要问了?你这么麻烦干嘛?大家的SEQ初始值为啥要为随机数呢?如果大家的SEQ都默认从0或者1开始,这样大家就不用向对方交换自己的SEQ值了嘛!仍然也可以解决数据包“顺序错乱”的问题啊!还设置一个随机数,真的是多此一举!

事情真的是这样的吗?SEQ可以不用随机值吗?接下来我们就来探讨一下这个问题。

 

SEQ的初始值为什么是随机的?


其实啊!TCP协议规定SEQ的初始值是随机值是有它的道理的,因为,这样能够保证TCP的安全性。

在TCP协议中,有一种关于它的攻击手段叫做“TCP序列猜测攻击”。它指的是攻击者企图预测被用来标识TCP连接中数据包的序列号,从而可以被用来伪造数据包。

攻击者希望能够正确的猜测出发送方发出的数据包的序列号,如果他们成功的话,他们有能力发送一个“伪造的数据包”给接收方,但是接收方却仍然以为数据包是从“发送方”发送出来的,即使这个“伪造数据包”事实上是来自于一个被攻击者控制的第三方主机。一个可能的做法是:攻击者监听发生在两个主机之间的“谈话”,然后使用同样的IP地址发送一个“伪造数据包”,通过分析,攻击者很有可能可以猜出正确的序列号。当IP地址和序列号都被攻击者知道后,接下来就是比“发送者”和“攻击者”谁发数据包的速度快了。对于攻击者的另一个做法是:当攻击者控制了连接后,他有能力发送一个“不需要response”的“伪造数据包”,因此可以借此发动“DDOS”攻击。

如果攻击者能够成功的发送“伪造数据包”,他有能力造成许多的破坏。例如,向一个已经存在的TCP连接中注入攻击者自己的数据,或者通过发送一个“RST”值为1的“伪造数据包”给接收端来提前终止TCP连接。

因此,TCP中的SEQ序列是保护TCP连接中不被“SEQ预测攻击”的一种重要保障手段,SEQ的值要求为随机值的原因也就清楚了。

 

抓取真实的TCP包


为了能够更加直观的了解TCP的“三次握手”过程,我们可以利用一款非常厉害的抓包工具:Wireshark。没有下载的同学可以下载到自己的电脑上抓包试一试,下面是我在一次HTTP访问请求中抓到的包(点击图片可放大观看):

 

其中,我们发现在3次TCP“成功握手”后,连接才建立起来了。这也说明了HTTP利用了底层的TCP协议。即HTTP使一种可靠的连接。

博客文章版权声明


  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值