TCP协议原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP协议原理相关的知识,希望对你有一定的参考价值。

参考技术A 一个数据包的生命过程:数据包如何送达主机、主机如何将数据包转交给应用、数据是如何被完整地送达应用程序

互联网,实际上是一套理念和协议组成的体系架构 。其中,协议是一套众所周知的规则和标准,如果各方都同意使用,那么它们之间的通信将变得毫无障碍。数据通信是通过数据包来传输的。如果发送的数据很大,那么该数据就会被拆分为很多小数据包来传输。之后再由接收方按照数据包中的一定规则将小的数据包整合成全部数据。

IP 是非常底层的协议,只负责把数据包传送到对方电脑,不负责该数据包将由哪个程序去使用。

数据包要在互联网上进行传输,就要符合网际协议(Internet Protocol,简称 IP)标准。计算机的地址就称为 IP 地址,访问任何网站实际上只是你的计算机向另外一台计算机请求信息。

简单理解数据传输过程就是:装包和拆包。 如果要想把一个数据包从主机 A 发送给主机 B,那么在传输之前,数据包上会被附加上主机 B 的 IP 地址信息,这样在传输过程中才能正确寻址。额外地,数据包上还会附加上主机 A 本身的 IP 地址,有了这些信息主机 B 才可以回复信息给主机 A。这些附加的信息会被装进一个叫 IP 头的数据结构里。IP 头是 IP 数据包开头的信息,包含 IP 版本、源 IP 地址、目标 IP 地址、生存时间等信息。

过程:

1、上层将含有“数据”的数据包交给网络层;

2、网络层再将 IP 头附加到数据包上,组成新的 IP 数据包,并交给底层;

3、底层通过物理网络将数据包传输给主机 B;

4、数据包被传输到主机 B 的网络层,在这里主机 B 拆开数据包的 IP 头信息,并将拆开来的数据部分交给上层;

5、最终,含有“数据”信息的数据包就到达了主机 B 的上层了。

基于 IP 之上开发能和应用打交道的协议,最常见的是“用户数据包协议(User Datagram Protocol)”,简称 UDP。负责将传输的数据包交给某一应用程序。

UDP 中一个最重要的信息是端口号,端口号其实就是一个数字,每个想访问网络的程序都需要绑定一个端口号。通过端口号 UDP 就能把指定的数据包发送给指定的程序了, 所以 IP 通过 IP 地址信息把数据包发送给指定的电脑,而 UDP 通过端口号把数据包分发给正确的程序。 和 IP 头一样,端口号会被装进 UDP 头里面,UDP 头再和原始数据包合并组成新的 UDP 数据包。UDP 头中除了目的端口,还有源端口号等信息。

为了支持 UDP 协议,我把前面的三层结构扩充为四层结构,在网络层和上层之间增加了传输层

过程:

1、 上层将数据包交给传输层;传输层会在数据包前面附加上 UDP 头,组成新的 UDP 数据包,再将新的 UDP 数据包交给网络层;

2、网络层再将 IP 头附加到数据包上,组成新的 IP 数据包,并交给底层;

3、数据包被传输到主机 B 的网络层,在这里主机 B 拆开 IP 头信息,并将拆开来的数据部分交给传输层;

4、在传输层,数据包中的 UDP 头会被拆开,并根据 UDP 中所提供的端口号,把数据部分交给上层的应用程序;

5、最终,含有信息的数据包就旅行到了主机 B 上层应用程序这里。

在使用 UDP 发送数据时,有各种因素会导致数据包出错,虽然 UDP 可以校验数据是否正确,但是对于错误的数据包, UDP 并不提供重发机制,只是丢弃当前的包 ,而且 UDP 在发送之后也无法知道是否能达到目的地。虽说 UDP 不能保证数据可靠性,但是传输速度却非常快 ,所以 UDP 会应用在一些关注速度、但不那么严格要求数据完整性的领域,如在线视频、互动游戏等

上文说到的使用 UDP 来传输会存在两个问题 :

1、数据包在传输过程中容易丢失;

2、大文件会被拆分成很多小的数据包来传输,这些小的数据包会经过不同的路由,并在不同的时间到达接收端,而 UDP 协议并不知道如何组装这些数据包,从而把这些数据包还原成完整的文件。

所以TCP协议很好地解决的这个问题。

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

1、对于数据包丢失的情况,TCP 提供重传机制;

2、TCP 引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件。

和 UDP 头一样,TCP 头除了包含了目标端口和本机端口号外,还提供了 用于排序的序列号 ,以便接收端通过序号来重排数据包

一个完整的 TCP 连接的生命周期包括了“建立连接”“传输数据”和“断开连接”三个阶段。

首先,建立连接阶段。这个阶段是通过“三次握手”来建立客户端和服务器之间的连接。TCP 提供面向连接的通信传输。面向连接是指在数据通信开始之前先做好两端之间的准备工作。所谓 三次握手 ,是指在建立一个 TCP 连接时,客户端和服务器总共要发送三个数据包以确认连接的建立。

其次,传输数据阶段。在该阶段,接收端需要对每个数据包进行确认操作,也就是接收端在接收到数据包之后,需要发送确认数据包给发送端。所以当发送端发送了一个数据包之后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的重发机制。同样,一个大的文件在传输过程中会被拆分成很多小的数据包,这些数据包到达接收端后,接收端会按照 TCP 头中的序号为其排序,从而保证组成完整的数据。

最后,断开连接阶段。数据传输完毕之后,就要终止连接了,涉及到最后一个阶段“ 四次挥手 ”来保证双方都能断开连接。

三次握手和四次挥手限于篇幅可看另一篇文章: TCP协议中 的三次握手和四次挥手

1、IP 负责把数据包送达目的主机。

2、UDP 负责把数据包送达具体应用(可能会丢包)。

3、而 TCP 保证了数据完整地传输 ,它的连接可分为三个阶段:建立连接、传输数据和断开连接。

完整的数据流程

TCP协议原理一

文章目录

一、TCP协议

我们的TCP协议相比于UDP协议复杂不少,今天我们就来一起学习一下TCP协议报文和原理

首先我们报头第一行里的端口号和UDP的端口号是一致的,都是用两个字节来表示。
32位序号和32位确认序号: 在这里先不给大家解释,等会我们再将TCP确认应答原理时会给大家讲到。
4位首部长度: 正是因为这个首部长度存在,我们的TCP的报头长度是可变的,而我们的UDP报头长度是8个字节固定死的,首先我们需要明白,我们选项之前的长度是固定的20字节,而我们首部长度可以调节的就是选项长度,我们首部长度的单位是4字节,选项长度等于首部长度 - 20字节,如果我们的首部长度是6,那么TCP报头就是24字节(选项相当于是4字节)。
**保留(6位): ** 我们这里的保留六位是为了以后扩展考虑的,因为我们的网络协议是一件成本较高的事情,就拿UDP来说,报文长度是2字节,因此一个包最大是64KB,如果我们想让它兼容更大一些的长度,理论可行但是实际操作极高,但是如果我们引入了保留字,我们后面TCP引入了新功能就可以使用这些保留字,不仅对报头结构影响极小,而且老设备也不需要升级也可兼容。

这个我们在后面讲原理的时候会介绍到。
16位窗口大小,16位紧急指针: 这些我们在后面会介绍到,16位窗口和我们的滑动窗口和流量控制原理有关。
16位校验和: 和我们的UDP校验原理相同,CRC,MD5,SHA1
选项: 选项(option),这个是对TCP一些功能扩展和TCP一些属性进行解释说明的。

TCP报文 = TCP报头 + TCP载荷(payload),载荷也就是我们的数据。

二、TCP工作机制

我们的TCP相比于UDP是一个比较复杂的协议,也包含很多机制,我们主要学习10大核心机制

1.确认应答

TCP的特点中有可靠传输的特性,确认应答就是实现可靠传输的核心机制。大家还需要明白,我们这里的可靠指的不是我们发送方百分百能将数据发送到接收方,而是尽可能的发过去即使没有发过去我们的发送方也能够知道。
比如我们有这样一个场景,一个宿舍中的A同学和B同学要通信。

我们这里的不上就称之为"应答报文",也叫ack(acknowledge).
当A同学收到不上的时候,就可以知道我们上号这个消息B同学已经看到了(证明没有丢包),如果我们一段时间后没有收到ack证明我们的消息大概率不见了(也就是丢包)。
TCP可靠传输主要就是靠这个确认应答机制来保证的,我们A给B发消息,B收到之后就会返回一个ack,A收到这个应答之后,就证明我们的数据顺利到达了(没有丢包)。
上述的通信是比较简单的单一通信,我们来看一个稍微复杂一点的。

同学A可能会连续发多条消息,发第二条消息时,不需要等待第一个消息的应答,我们的B同学收到消息立马回复,我们在网络通信时,很有可能发生后发先至,因为我们的网络环境是错综复杂的,很有可能发的晚的消息先到的情况。
上面的情况是正常的情况,那么我们来看一个不正常的情况。

因为这种后发先至的情况,我先收到了上,后收到了不上,这样我们的应答错乱后表示的含义就截然不同了。
首先网络上后发先至这种情况是客观存在无法避免的,我们应该考虑的是如何应对这种情况所带来的异义。
我们采取的办法是给传输的数据和应答报文编号。

当我们引入序号后,即使出现先发后至的情况,消息的顺序即使乱了,我们通过序号也可以明确将每条应答报文与其对应了。


我们任何一条数据(包括应答报文)都是有序号的,确认序号只有应答报文有,是否为应答报文取决于ACK这个标志位是否为1,如果为1证明是应答报文,如果是0表示不是应答报文。
我们实际上的TCP序号并不是简单的1,2这种方式编号,我们TCP是面向字节流的,我们的TCP序号也是按照字节来编号的。

假设我们的一条数据长度是1000个字节,我们的数据是由1开始的,此时我们第一个字节编号就是1,第二个字节编号就是2依次,但是我们前1000个字节都是属于同一TCP报文,因此我们TCP报头里记录的序号就是第一个字节的序号,也就是1。
当我们发送第二条数据,序号就是1001了,我们的TCP字节序号是依次增加的,起始字节序号是上一个数据的最后一个字节,我们TCP报头只需写TCP头一个字节序号即可,在根据TCP报文长度即可知道TCP的每一个字节的序号。

我们应答报文里的确认序号中的1001就是刚才尾字节1000基础+1的结果,1001所表示的含义有两个:
1.小于1001的数据都已经确认收到了
2.发送方接下来应该从1001这个序号开始发送数据

结论: TCP可靠传输最主要是通过确认应答机制保证的,我们不仅可以让发送方清楚的知道是否传输成功,并且通过序号和确认序号对多组数据进行了详细的区分。

2.超时重传

我们刚才在讨论确认应答的时候,只是在一个理想的传输场景下讨论的,那如果在传输过程丢包了呢?
首先丢包有两种情况: 1. 发送的数据丢了 2.应答报文ack丢了,站在我峨嵋你发送方的角度,这两种情况都认为是丢包了,正在因为这种丢包的可能性发送,我们TCP就引入了重传机制,我们在丢包的时候就要重新发一遍相同的数据,那么如何判断是丢包还是ack正在返回路上?
我们TCP直接引入一个时间阈值,在发送数据后,就会开始倒计时等待ack,如果时间超过时间阈值也没收到ack,统一认为丢包。
超时重传: 接收方在一定时间没有响应,发送方就重新发送一份相同的数据。
时间阈值: 超时时间具体是多少,这个我们不具体讨论是多少,根据场景业务而定,是可配置的。

这种情况比较形象,发送方发送的数据丢包了,在一定的时间后重新传输一次,然后收到ack之后就视为一次成功的传输。

但是当ack丢包时,对于我们主机B而言,1 -1000的数据接收了两次,这种情况就比较麻烦了,但是我们TCP对于重复的数据传输,具有去重功能,我们TCP存在一个发送缓冲区和接收缓冲区接收缓冲区相当于是接收方操作系统内核的一段内存,我们在网络编程的时候学习的socket的对象中都有一个接收缓冲区,我们主机B的网卡接收到数据之后,将数据放到主机soekct对象对应的接收缓冲区中,我们可以将这个缓冲区想象成一个优先级阻塞队列,我们根据序号可以将数据进行去重排序,如果有相同的数据就将后到的数据丢弃,排序就可以应对我们的后发先至情况。
结论: 由于我们去重与排序机制的存在,发送方只要发现在一定时间阈值内ack没有到达,就会重新发一份数据,数据即使重复乱序,我们的接收方都可以处理,去重和排序机制都依赖TCP报头的序号。
相信有一些善于思考同学又会问了,要是我们重传的数据有丢包了呢?
首先我们丢包是一件概率比较低的事件,连续重传丢包的概率更低,因此我们在重传到一定次数后,就不会再死磕,就会认为网络出现故障,TCP就会尝试重置连接,如果重置还是无效,就会彻底断开连接。
TCP的可靠传输: TCP的可靠传输通过 确认应答 和 超时重传机制保证的,两者相互配合,共同保证了TCP的可靠性。

3.连接管理

连接在不同的场景表示不同的含义,在我们TCP协议这里,表示的是维护一些信息(四元组)的空间。

我们TCP建立连接并不是指通过一根线将两者连接起来,而是将这两部分信息维护好,简单来说A要能通过这部分信息找到B,B能通过这部分信息找到B,我们称保存好这部分信息的空间为连接,删除这部分信息,称之为断开。

三次握手

我们仍然拿出A同学和B同学,刚上大学A同学和B同学想通过游戏加深一下感情,但是又不知道对方玩什么游戏,于是有了以下的场景:

首先A向B发出了询问,然后B回应了A,此时双方都知道了B玩王者农药,但想要一起组队玩王者农药,B需要明确A是否也玩,于是B又发送了一次请求,这时候A知道了B玩,B知道了A也玩,这个时候他们才能一起上号打游戏,相当于建立连接成功。
我们把上述过程的每次通信形象的称为一次挥手,但实际上我们有两次是可以合并成一次的。

我们所谓的三次握手的本质上是四次交互,双方各自需要发送一个建立的请求,然后收到一个ack,实际上是有四次的信息交互,但是中间两次是可以合并的,因此就构成了三次握手
如果不合并,可以吗?
不可以,如果我们从三次变成四次就会多封装分用一次,成本更高,就好比宿舍倒垃圾,本来可以一个大袋子一次性倒,一个是用两个小袋子分两次倒。

两次握手可以完成建立连接吗?

如果少了最后一次握手,站在A的角度,他已经知道了B玩王者农药,但是站在B的角度,他不知道A玩不玩王者农药。
有的同学可能会说,A能问你A肯定玩呀,虽然这个情况也有可能,但是TCP这里不适用,因为三次握手还有一个重要作用:验证通信双方各自的发送和接收能力是否正常
比如我们两个人在打王者农药,组队开麦,A和B能够顺利交流,需要有两个保证,就是A和B的麦和音响都是好的。

第一次通信,当B听到"你可以听到吗?" B知道了 A的麦是正常的,B的音响是正常的。 A什么都不知道。
第二次通信,当A听到"可以,你呢", B知道了A的麦是正常的,B的音响是正常的。A知道了A的麦和音响以及B的麦和音响都是正常的。
第二次通信之后,A以及知道了双方都满足了条件,但B知道的信息还不全,需要进行第三次通信。
第三次通信,当B听到"可以"时,证明我们A和B都知道了自己的麦和音响和对方的麦和音响是正常的,可以正常交流了。
三次握手的作用:
1.让通信双方建立相互的认可
2.验证双方的发送与接受能力是否正常
3.在握手的过程,双方协商一些参数。

我们客户端主动给服务器发送的建立连接请求称为SYN同步报文段.


这些都是TCP的状态,不同的TCP状态主要体现我们的TCP在干什么,三次握手中我们主要学习两个重要状态:
1.LISTEN服务器状态:表示我们的服务器以及转杯就绪,随时可以与客户端建立连接。
2.ESTABLISHED::这个状态我们客户端服务器都有,当我们进行两次握手后,我们的客户端就已经认为进行成功建立连接,于是进入了ESTABLISHED状态,我们的服务器只有当三次握手进行完毕后才认为成功的建立了连接,进入了ESTABLISHED状态,当我们成功建立连接之后就可以进行通信了。
上述描述的三次握手一次性记住不太容易,大家需要记住主要的流程:

四次挥手

握手和挥手都是形象的叫法,指的是客户端服务器之间的交互,四次挥手指的是通信双方给对方发一个断开连接的请求,在各自给对方一个回应。
这里我们同样是A同学和B同学打了一下午王者农药输了一下午:

这里需要注意,我们在断开连接的过程中,中间两次通常是不能合并的。

我们三次握手之所以中间两次可以合并,是因为它两是属于同一时机的,具体来说,三次握手的三次交互过程是系统内核完成的,服务器内核收到了syn之后会立即发送ack也会立即发送syn。
这就好比于我们在同一家店铺买东西,我们的三次握手中间的两次就相当于是同一时间段在一家店铺买的两样东西,所以可以通过一个包裹发送这两样东西,而我们四次挥手中间两次,则是在不同的天数买的,比如2月9号买了个牙刷,2月11买了个牙膏,则不能够通过一个包裹,必须通过两个包裹发送。

我们的FIN发起不是有内核控制的,而是我们客户端调用socket的close方法(进程退出)才会触发FIN,我们服务器的ack是有内核控制的,收到FIN之后立即返回ack,而我们服务器的FIN是我们服务器执行到socket的close方法(或进程退出)才会触发FIN。
因此大家注意到,我们服务器ack和FIN之间只有一个时间差的,而这个时间差的大小由我们的代码所决定,我们可以发开我们之前写的TCP客户端服务器回显服务器看一看。

我们可以看到这里的break决定着循环结束,而这里的break能够执行到取决于我们的hasNext为false,因就是流对象读到了EOF(文件结束标记),这里能读到EOF是因为内核收到了客户端发送来的FIN数据报,虽然我们的客户端程序没有显示的写close方法,但是当我们客户端进行退出的时候也会执行socket close会触发FIN。

当我们的循环执行结束之后,会执行到finally里的close方法,相当于我们服务器给客户端发送的FIN,我们上述代码里循环结束后立即FIN,ack与FIN之间的时间间隔相对就比较短,也就有可能包裹成一个,但是如果时间间隔比较长就不可以了,比如这样:

我们的代码在close之前进行了一系列操作,这样我们的FIN和ack时间间隔就比较久了,也就无法合并成一个了。

同样的我们断开连接中的FIN在报头中也有标志,FIN为1证明是断开连接的请求报文。

我们TCP四次挥手当中也有许多状态,我们这里重点学习两个TCP状态:
1.CLOSE_WAIT: 出现在被动连接的一方,等待关闭(等待socket调用close方法),大家需要注意的是,建立连接的过程中一定是客户端主动发起请求,断开连接可能是客户端也可能是服务器。
TIME_WAIT: 出现在主动发起断开请求的一方法,这里我们客户端是主动断开的一方,当客户端进行TIME_WAIT状态时,客户端认为四次挥手已经挥完了,这里的TIME_WAIT要保持TCP状态保持一会不要立即释放。
为什么我们的TIME_WAIT要等待一会不要立即释放连接?
因为我们最后一个ack虽然已经发出去了,但是仅仅是发出去了,到没到我们得打一个问号?万一这个ack丢包了呢,我们TIME_WAIT会在发完ack之后等,如果没有接收到重传的FIN,就认为最后一个ack没有丢包然后释放连接。

站在我们服务器角度来说,当我们ack发送了之后,进入TIME_WAIT状态时,相当于是四次挥手已经完成了一样,没有客户端的活了,虽然看起来完了,但是是建立在一切顺利的前提下,如果出现了丢包等情况,客户端就没有完成工作,我们的TIME_WAIT就是这些工作但的保证。
那么TIME_WAIT保持多久,才真正释放呢?
我们这里等待的时间为2MSL ,如果TIME_WAIT维持了2MSL都没用收到重传的FIN,就认为我们的ack顺利到达了。
那么MSL指的又是什么呢?
指的是,互联网上两个结点之间,消耗传输时间的最大时间。至于这个MSL通常大概是60s,在这里大家需要注意,我们这里的MSL无论如何定义都不可避免一些特殊情况,因此我们的MSL相当于是一个经验值,绝大多数情况下数据包传输时间都不会超过MSL。

以上是关于TCP协议原理的主要内容,如果未能解决你的问题,请参考以下文章

TCP协议原理

TCP协议设计原理

TCP协议原理一

TCP/IP协议原理及应用

TCP/IP协议的三次握手及实现原理

TCP/IP协议原理与介绍