众所周知,TCP在建立连接时需要经过三次握手。许多初学者经常对这个过程感到混乱:SYN是干什么的,怎么一会儿是1一会儿是0?怎么既有大写的ACK又有小写的ack?为什么ACK在第二次握手才开始出现?初始序列号isn有什么讲究?isn和seq有什么关系?ack的值到底是什么?
别慌,别着急,看完这篇文章,我相信上述问题对你来说就会迎刃而解。
我将TCP三次握手所涉及到的具体操作,总结为“设标志位,发序列号”。这里先告诉你一下,一般来说标志位的名称全部大写,序列号的缩写名称全部小写。
在开始讲解之前,我们先来看一下TCP段头的结构:
是不是感觉头昏眼花,这么多英文,这么多组成!其实,三次握手的过程只涉及到两个序列号(Sequence、Acknowledgement Sequence)和两个标志位(ACK、SYN)。注意我这里的大小写结构,与上图是完全对应的。下面我将分别讲解这些字段的含义。
先来说序列号。序列号包括seq和ack,seq是发送序列号,ack是确认序列号。
Sequence,缩写为seq,中文名为序列号,定义如下:The Sequence number indicates the position in the byte stream of the first byte in the TCP Data field. For example, if the Initial Sequence number is 1,000 and this is the first segment, then the Sequence number is 1,000. If the segment is 500 bytes long, then the sequence number in the next segment will be 1,500 and so on. 所以我们可以把seq理解为发送序列号。注意,在上述的定义中提到了Initial Sequence number(缩写isn,中文名初始序列号),你可能会感到疑惑,这和seq有什么关系?其实,这也是seq,只不过是第一次握手时用的序列号,所以叫初始序列号。发送端(客户端)和接收端(服务器端)的初试序列号都是随机生成的。初试序列号必须随机生成,计算机网络的设计者并不是随随便便地就制定了这项原则,随机是有其道理的。
为了了解isn的设计思想,我们首先需要知道TCP的“身份证”是什么。A TCP connection is uniquely identified by five pieces of information in the TCP and IP headers. The IP source and destination addresses uniquely identify the end points, and the IP Protocol ID for TCP tells us the connection is TCP. The TCP source and destination ports identify they application processes on the end hosts. Together, at any instant, all 5 fields uniquely identify the TCP connection Internet-wide. 即,一个TCP连接的ID是由源端口地址、目的端口地址、源IP地址、目的IP地址共同组成的。
假设有两台主机A和B建立了一个TCP连接,A向B发送了一些数据后,连接断开了。我们继续假设在连接断开前,有一些报文没有正常到达B ,而是一直处于传输网络中(这是有可能的,因为路由器会毫无先兆地缓存或者丢弃任何的数据包,并且如果路由器转发表出现了错误,数据包也会在网络里进行不必要的兜兜转转)。 之后,AB之间又建立了一个连接且ID四元组跟上次的连接一样。此时,如果上一个连接的报文姗姗来迟,到达了B,且序列号在B的合理区间内,那么B 会以为这个报文是本次连接的正常报文。这不就出错了嘛,数据发生了混乱!所以,我们需要随机的isn。
如果你还是没看懂,不妨集思广益。下面是网友们的解答,可以结合着看一看:
(甲)ISN不同是为了防止老连接的包/伪造包干扰新连接。举个例子来说1,2两个连接先后建立,并且四元组相同,在此情况下如果ISN固定的话,很有可能连接1某个丢失的包在连接1关闭,连接2已经建立的情况下突然被网络转回来了,因为四元组相同,此时协议栈会以为该包是连接2的包,轻则会导致通讯双方重传,重则导致数据错乱或连接不正常关闭,所以需要开随机的ISN,理论上该方法也只能大大降低问题出现的概率,而不能完全避免,tcp连接主动关闭时time_wait时间也是为了尽量避免上述情况。
(乙)举个例子,如果初始化序列号isn是固定的,我们来看看会出现什么问题。假设isn固定是1,Client和Server建立好一条TCP连接后,Client连续给Server发了10个包,这10个包不知怎么被链路上的路由器缓存了(路由器会毫无先兆地缓存或者丢弃任何的数据包,而且如果路由器转发表出现了错误,包也会在网络里进行不必要的兜兜转转),这个时候碰巧Client挂掉了,然后Client用同样的端口号重新连上Server,Client又连续给Server发了几个包,假设这个时候Client的序列号变成了5。接着,之前被路由器缓存的10个数据包全部被路由到Server端了,Server给Client回复确认号10,这个时候,Client整个都不好了,这是什么情况?我的序列号才到5,你怎么给我的确认号是10了,整个都乱了。所以现在的方法是在一个基准值得上产生一个随机值。
(丙)如果TCP在建立连接时每次都选择相同的、固定的初始序号,那么设想以下的情况: (1)假定主机A和B频繁地建立连接,传送一些TCP报文段后,再释放连接,然后又不断地建立新的连接、传送报文段和释放连接。 (2)假定每一次建立连接时,主机A都选择相同的、固定的初始序号,例如,选择1。 (3)假定主机A发送出的某些TCP报文段在网络中会滞留较长的时间,以致造成主机A超时重传这些TCP报文段。 (4)假定有一些在网络中滞留时间较长的TCP报文段最后终于到达了主机B,但这时传送该报文段的那个连接早已释放了.而在到达主机B时的TCP连接是一条新的TCP连接。 这样,工作在新的TCP连接下的主机B就有可能会接受在旧的连接传送的、已经没有意义的、过时的TCP报文段(因为这个TCP报文段的序号有可能正好处在现在新的连接所使用的序号范围之中)。结果产生错误。 因此,必须使得迟到的TCP报文段的序号不处在新的连接中所使用的序号范围之中。 这样,TCP在建立新的连接时所选择的初始序号一定要和前面的一些连接所使用过的序号不一样。因此,不同的TCP连接不能使用相同的初始序号。
(丁)(源IP,源端口,目的IP,目的端口,协议号),这五个要素组成一个五元组。协议号都是TCP的话说是四元也行。不同的五元组,它们的SN完全是独立的空间,互相不干扰,所以是多少都是可以的。但是这里有个安全问题,如果你使用固定的其实,那么一个攻击者就有可能猜出你的SN号序列,然后插入到你的TCP连接当中。我们知道IP协议并不安全,一个发送者可以很容易伪造自己的源IP地址来发送数据包,即使他没有办法在你和目标中间建立侦听,他也可以通过推测你们两者的ISN来发送可以混进TCP连接的数据,造成很严重的安全问题。所以现代操作系统会根据不同的五元组,创建独立的ISN空间,同一时间建立的五元组不同的连接,会使用不同的ISN,这是为了安全。
Acknowledgement Sequence,缩写为ack,中文名为确认序列号,定义如下:The Acknowledgment sequence number tells the other end which byte we are expecting next. It also says that we have successfully receivedevery byte up until the one before this byte number. For example, if the Acknowledgment Sequence number is 751, it means we have received every byte up to and including byte 750. Notice that there are sequence numbers for both directions in every segment. This way, TCP piggybacks acknowledgments on the data segments traveling in the other direction. 也就是说,ack的值用于通知另一端“我接下来想要的数据”,同时还告诉另一端“我已经收到了你刚刚发过来的所有数据”。举个不是很恰当的例子,这就像一个不太正常的人去吃自助餐,念叨着“刚刚拿来的五花肉、肥牛和鸡米花我都吃完了,接下来我想继续吃点水果”。
再来说标志位,在TCP段头中有六种标志位(Flag),我们通常将他们的名字全部大写。注意,这很重要,标志位的名称全部大写!所以,你在网上看到的许多三次握手示意图里,SYN和ACK都是标志位。
ACK的定义:The ACK flag tells us that the Acknowledgement sequence number is valid and we are acknowledging all of the data up until this point. 从中我们可以知道,只有当ACK这个标志位为1的时候,ack序列号值才有效。你可以这样理解:只要有ack的地方,都需要设置ACK=1,ACK就像是ack的开关一样。
SYN的定义: The SYN flag tells us that we are signalling a synchronize, which is part of the 3way handshake to set up the connection. SYN是synchronize(同步)的缩写,在第一次握手和第二次握手时,SYN=1,在第三次握手时,SYN=0。这就像是say hi,两个人在路上遇到,彼此打了个招呼后,就不再打招呼了,不然,两人每说一句话,都先来一句hi,这也太奇怪了!
最后,我们再来看一下三次握手示意图,几张示意图结合起来看,我懒得自己画了:
再配一个TCP三次握手过程的文字描述:
第一次握手:客户端将标志位SYN置为1,随机产生一个值序列号seq=x,并将该数据包发送给服务端,客户端 进入syn_sent状态,等待服务端确认。
第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和 ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认连接请求,服务端进入syn_rcvd状态。
第三次握手:客户端收到确认后检查,如果正确则将标志位ACK为1,ack=y+1,并将该数据包发送给服务端,服务端进行检查如果正确则连接建立成功,客户端和服务端进入established状态,完成三次握手,随后客户端和服务端之间可以开始传输数据了。
怎么样,是不是特别清楚了!如果不是特别清楚的话,建议再读一遍。如果读了还是不清楚,那你就洗洗睡吧。
注:本文参考了
https://www.cnblogs.com/dormant/p/5422124.html
https://www.zhihu.com/question/49794331
https://www.zhihu.com/question/53658729/answer/255221757
TCP协议中的三次握手和四次挥手(图解)_Simple life-CSDN博客_三次握手