Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCP的握手分手八卦细节 #24

Open
wolfdu opened this issue Jan 26, 2018 · 1 comment
Open

TCP的握手分手八卦细节 #24

wolfdu opened this issue Jan 26, 2018 · 1 comment

Comments

@wolfdu
Copy link
Owner

wolfdu commented Jan 26, 2018

https://wolfdu.fun/post?postId=5a6aba12b890b156d0fae649

之前对TCP的了解仅限于几次握手和分手,但是其中的细节就不是很清晰,所以整理学习的相关内容,加深理解

TCP是什么?

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

它是一个非常复杂的协议,这里只是简述一些相关信息,如果想系统学习整个协议还是需要静下心来读一读TCP/IP详解 卷1:协议

OSI参考模型

首先我们应该先了解下OSI参考模型:
OSI(Open System Interconnect),即开放式系统互联,一般叫OSI参考模型。
OSI参考模型图

每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。各层所提供的服务与这些服务是怎么实现的无关。

下面列出每层的作用以及对应的协议:

OSI中的层 功能 TCP/IP协议族
应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
表示层 数据格式化,代码转换,数据加密 没有协议
会话层 解除或建立与别的接点的联系 没有协议
传输层 提供端对端的接口 TCP,UDP
网络层 为数据包选择路由 IP,ICMP,OSPF,EIGRP,IGMP
数据链路层 传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,MTU
物理层 以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802,IEEE802.2

我们需要知道就是TCP在网络OSI的七层模型中的第四层——传输层(Transport Layer),IP在第三层——网络层(Network Layer),ARP在第二层——数据链路层(Data Link Layer),在第二层上的数据,我们叫Frame,在第三层上的数据叫Packet,第四层的数据叫Segment。

那么TCP 协议的作用是什么?

简而言之,TCP提供可靠的连接服务,保证数据通信的完整性和可靠性,防止丢包。

如果我们想要更进一步的了解TCP协议,那么我们就需要了解该协议中各个实现细节,我们先看看TCP数据包里面有些什么。

TCP数据包

我们知道当数据数据从应用层发下来,会在每一层都会加上头部信息,进行封装,然后再发送到数据接收端。
应用数据和各层协议头组成的数据包
以太网数据包(packet)的大小是固定的,最初是1518字节,后来增加到1522字节。其中, 1500 字节是负载(payload),22字节是头信息(head)。
IP 数据包在以太网数据包的负载里面,它也有自己的头信息,最少需要20字节,所以 IP 数据包的负载最多为1480字节。

TCP 数据包在 IP 数据包的负载里面。它的头信息最少也需要20字节,因此 TCP 数据包的最大负载是 1480 - 20 = 1460 字节。由于 IP 和 TCP 协议往往有额外的头信息,所以 TCP 负载实际为1400字节左右。

TCP Head

我们先了解TCP协议Head部分的格式:
TCP-Head

理解其中每个字段含义是我们了解TCP的基础知识,所以耐心点我们一个一个了解吧。

Source Port & Destination Port

Source Port 和 Destination Port分别占用16位,表示源端口号和目的端口号。

用于区别主机中的不同进程,而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址(Source Address)和目的IP地址(Destination Address)就能唯一的确定一个TCP连接。

IP-Head格式:
IP-Head

Sequence Number

该字段用来标识从TCP发端向TCP收端发送的数据字节流中的的第一个数据字节在数据流中的序号,表示TCP 数据包的编号,用来解决网络包乱序(reordering)问题。

一个包1400字节,那么一次性发送大量数据,就必须分成多个包。比如,一个 10MB 的文件,需要发送7100多个包。
发送的时候,TCP 协议为每个包编号(sequence number,简称 Seq),以便接收的一方按照顺序还原。万一发生丢包,也可以知道丢失的是哪一个包。

第一个包的编号是一个随机数。为了便于理解,这里就把它称为1号包。假定这个包的负载长度是100字节,那么可以推算出下一个包的编号应该是101。这就是说,每个数据包都可以得到两个编号:自身的编号,以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。

Acknowledgment Number & Window

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

  • Window: 窗口大小,也就是有名的滑动窗口,用来进行流量控制(这块内容比较多需要单独开坑,这里简单带过)。

从一个场景来了解这两个字段:
服务器发送数据包,当然越快越好,最好一次性全发出去。但是,发得太快,就有可能丢包。带宽小、路由器过热、缓存溢出等许多因素都会导致丢包。线路不好的话,发得越快,丢得越多。

最理想的状态是,在线路允许的情况下,达到最高速率。但是我们怎么知道,对方线路的理想速率是多少呢?答案就是慢慢试。
TCP 协议为了做到效率与可靠性的统一,设计了一个慢启动(slow start)机制。开始的时候,发送得较慢,然后根据丢包的情况,调整速率:如果不丢包,就加快发送速度;如果丢包,就降低发送速度。

例如刚开始通信的时候,发送方一次性发送10个数据包,即"发送窗口"的大小为10。然后停下来,等待接收方的确认,再继续发送。

其中的发送窗口大小就是Window size而确认消息就是ACK

ACK 携带两个信息:

  1. 期待要收到下一个数据包的编号
  2. 接收方的接收窗口的剩余容量

发送方有了这两个信息,再加上自己已经发出的数据包的最新编号,就会推测出接收方大概的接收速度,从而降低或增加发送速率。

由于 TCP 通信是双向的,所以双方都需要发送 ACK。两方的窗口大小,很可能是不一样的。
我们通过一个实例来看一看其中的Seq和ACK变化:

TCP-flow

主要观察每次通信间Seq,ACK的变化和ACK-Len的关系。

TCP Flags

TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为URG,ACK,PSH,RST,SYN,FIN。

  • URG:此标志表示TCP包的紧急指针域有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据
  • ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中,有两个取值:0和1,为1的时候表示应答域有效,反之为0
  • PSH:这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队
  • RST:这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包
  • SYN:表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行TCP的三次握手
  • FIN:表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描

需要注意的点:

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

  • SYN(SYNchronization) : 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。

  • FIN (finis)即完,终结的意思, 用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

TCP三次握手,四次分手

吼啦,基础信息都了解了一些了,接下我们就来看看经典的三次握手和四次分手吧!
下面有两个对照图,可能在了解后面内容需要不时的对照翻看,建议可以手动画一画,非常有帮助哦(●>∀<●)

以下是TCP建立连接,数据传输,断连接的对照图:

TCP建立连接,数据传输,断连接的对照图

TCP协议状态机对照图:
TCP协议状态机对照图

三次握手

为什么建立连接要进行3次握手呢?

先来看看这三次握手TCP干了些啥,每个步骤可以对照上面对照图进行分析。

  1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x。然后,客户端进入SYN_SEND状态,等待服务器的确认。
  2. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(seq+1)。同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y。服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态。
  3. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

下面从TCP首部字段的变化来了解其中的意义:

Sequence Number

我们可以发现三次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN(同步序号),全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个序号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。

关于ISN即seq的初始化,我们前面在介绍seq的时候我们说过这个初始数值为一个随机数。

为什么不能hard code 呢?

用一个例子来解释:
如果连接建好后始终用1来做ISN,如果client发了30个segment过去,但是网络断了,于是 client重连,又用了1做ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而Server端认为client端的这个号是30了。这样就全乱了。所以需要一个不重复的随机数来作为初始值。

Acknowledgment Number

从设置Acknowledgment Number的值我们可以发现:为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次(第一次和第二次)握手,为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次(第二次和第三次)握手。

其实前两次握手就可以建立连接,但是为什么要进行第三次握手呢?

考虑这样一种场景:
client端发送连接请求,但是由于请求报文丢失没有收到确认报文,于是client再次发送连接请求,这次正常进行了连接,传输数据,断开连接 。
如果client第一次发送的报文是因为某个网络结点长时间的滞留了,在第二次连接断开后发送到了server端,server端会如何处理呢?对比下面两种情况:

  1. 正常三次握手的情况:server端收到报文会认为client发起了连接请求,于是向client发出确认报文同意连接。但是client并没有发出请求,所以不会应答。那么server等待连接超时后就会断开了。
  2. 采用两次握手建立连接情况:server端收到报文后就会与client建立连接,但是client并没有发送请求,所以也不会发送数据,server会一直等待client发送数据。这样就造成了server端的资源浪费。

那么可以将第三次握手理解为,防止server端因为一直等待造成的资源浪费的措施。

番外篇

另一种解释三次握手,可以开阔一下思路:

这个问题的本质是,信道不可靠,但是通信双发需要就某个问题达成一致。而要解决这个问题,无论你在消息中包含什么信息, 三次通信是理论上的最小值。 所以三次握手不是TCP本身的要求, 而是为了满足"在不可靠信道上可靠地传输信息"这一需求所导致的。请注意这里的本质需求,信道不可靠, 数据传输要可靠。 三次达到了, 那后面你想接着握手也好,发数据也好, 跟进行可靠信息传输的需求就没关系了。 因此,如果信道是可靠的,即无论什么时候发出消息,对方一定能收到,或者你不关心是否要保证对方收到你的消息,那就能像UDP那样直接发送消息就可以了。

四次分手

客户端和服务器通过三次握手建立连接后,开始传输数据,数据传输完毕后。客户端想要断开连接,这里就要进行四次分手。

老套路,先看四次分手聊了些什么,对照状态机当然是少不了(其中的seq和ACK数值使用对照图内容):

  1. 第一次分手:client向TCP发出连接释放报文段,并停止发送数据,主动关闭TCP连接。client设置seq = x + 2ACK = y + 1,将连接释放报文首部终止控制位FIN置为1。此时,client进入FIN_WAIT_1(终止等待状态1)状态,等待server确认。
  2. 第二次分手:server收到了连接释放报文后即发出确认报文,ACK = x + 3(为seq加1)。server进入CLOSE_WAIT(关闭等待)状态。此时TCP连接处于半关闭状态,即client已经没有数据要发送了,但是server发送数据client任然接受,也就是server到client方向连接还没有关闭。client收到来自server的确认报文ACK后,就进入FIN_WAIT 2(终止等待2)状态,等待server发送连接释放报文
  3. 第三次分手:server已经没有数据要发送了,通知TCP释放连接。server发送释放报文段,设置FIN为1,seq = y + 1和上次发送过的ACK = x + 3。然后server进入LAST_WAIT(最后确认状态)状态,等待client确认。
  4. 第四次分手:client收到server发送的连接释放报文段后,必须发出确认报文。client在报文中ACK = y + 2seq = x + 3然后进入TIME_WAIT状态。server收到client确认报文段以后,就关闭连接CLOSED。此时,client等待2MSL后依然没有收到回复,则证明server端已正常关闭,那么,client也关闭连接CLOSED。

这里我们要注意client进入TIME_WAIT后会等待2MSL后才会进入CLOSED状态。

什么是MSL?

MSL叫做最长报文寿命(Maxximum Segment Lifetime),RFC793建议设置为2分钟,Linux设置成了30s,TCP允许不同实现根据情况设置更小的时间。

为什么要等待2MSL?

要等待2MSL有两个原因:

1.保证client发送的最后一个ACK报文能够到达server端。

考虑这样一个场景:

如果client发送的最后一个ACK报文丢失了,我们看看client等待2MSL和不等待2MSL的区别

  • client不等待2MSL:client发送完ACK报文就进入CLOSED状态,那么server就无法正常的进入CLOSED状态了。
  • client等待2MSL:client发送完ACK报文进入TIME_WAIT状态,由于server无法收到ACK报文会超时重传FIN+ACK报文,那么client就会重新发送ACK报文并重启2MSL计时器开始等待。
2.有足够的时间让这个连接不会跟后面的连接混在一起

防止像之前提到因为滞留在某个网络节点的报文出现,影响其他连接。所以在2MSL后本连接持续时间内产生的所有报文都会从网络中消失了。

Simultaneous Close

如果两边同时断连接(Simultaneous close),那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。下图是双方同时断连接的示意图(你同样可以对照着TCP状态机看):
tcpclosesimul

总结

当然以上这些内容只是简单的了解了TCP一些基础知识,但是为后面继续深入学习了解打下了基础。

通过以上的整理基本能够了解如下内容:

  • TCP首部基本字段的含义以及作用
  • TCP三次握手过程以及为什么要进行三次握手
  • TCP四次分手的过程

TCP是门古典的基础技术还有很多地方值得我去思考和学习,这仅仅是一个开始。

看到估计也累了,就到这里吧,梳理下来知识点不多,但是还是很有收获的,关于计算机网络这一块还有很多东西要去学习。

参考文章:
计算机网络
通俗大白话来理解TCP协议的三次握手和四次分手
TCP 协议简介
TCP/IP Reference
Understanding TCP Sequence and Acknowledgment Numbers
TCP 的那些事儿(上)

若文中有知识整理错误或遗漏的地方请务必指出,非常感谢。如果对你有一丢丢帮助或引起你的思考,可以点赞鼓励一下作者=^_^=

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants