本文内容如下:
      1)TCP协议概念
      2)TCP头部结构和字段介绍
      3)TCP流量控制
            滑动窗口
      4)TCP拥塞控制
           慢启动、拥塞避免、快重传、快恢复

一、TCP概念

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议。

首先来看看OSI的七层模型

我们需要知道:

  1、TCP工作在网络OSI的七层模型中的第四层——传输层,

  2、IP在第三层——网络层,

  3、ARP 在第二层——数据链路层;

  同时,我们需要简单的知道,数据从应用层发下来,会在每一层都会加上头部信息,进行 封装,然后再发送到数据接收端。

  这个基本的流程你需要知道,就是每个数据都会经过数据的封装和解封 装的过程。

  在OSI七层模型中,每一层的作用和对应的协议如下:

二、TCP头部结构和字段介绍

从上面图片可以看出,TCP协议是封装在IP数据包中。

下图是TCP报文数据格式。TCP首部如果不计选项和填充字段,它通常是20个字节。

下面分别对其中的字段进行介绍:

源端口和目的端口

各占2个字节,这两个值 加上 IP首部中的  “源端IP地址”  和   “目的端IP地址"   唯一确定一个TCP连接。有时一个IP地址和一个端口号也称为socket(插口)。

序号(seq)

占4个字节,是本报文段所发送的数据项目组第一个字节的序号。在TCP传送的数据流中,每一个字节都有一个序号。

   例如,一报文段的序号为300,而且数据共100字节,则下一个报文段的序号就是400;序号是32bit的无符号数,序号到达2^32-1后从0开始。

确认序号(ack)

占4字节,是期望收到对方下次发送的数据的第一个字节的序号,也就是期望收到的下一个报文段的首部中的序号;

   确认序号应该是上次已成功收到数据字节序号+1。只有ACK标志为1时,确认序号才有效。

数据偏移

占4比特,表示数据开始的地方离TCP段的起始处有多远。实际上就是TCP段首部的长度。

  由于首部长度不固定,因此数据偏移字段是必要的。数据偏移以32位为长度单位,

也就是4个字节,因此TCP首部的最大长度是60个字节。即偏移最大为15个长度单位=1532位=154字节。

保留

6比特,供以后应用,现在置为0。 6个标志位比特: 

① URG:当URG=1时,注解此报文应尽快传送,而不要按本来的列队次序来传送。与“紧急指针”字段共同应用,紧急指针指出在本报文段中的紧急数据的最后一个字节的序号,

使接管方可以知道紧急数据共有多长。

② ACK:只有当ACK=1时,确认序号字段才有效;

③ PSH:当PSH=1时,接收方应该尽快将本报文段立即传送给其应用层。

④ RST:当RST=1时,表示出现连接错误,必须释放连接,然后再重建传输连接。复位比特还用来拒绝一个不法的报文段或拒绝打开一个连接;

⑤ SYN:SYN=1,ACK=0时表示请求建立一个连接,携带SYN标志的TCP报文段为同步报文段;

⑥ FIN:发端完成发送任务。

窗口

TCP通过滑动窗口的概念来进行流量控制。设想:在发送端 发送数据的速度 很快,而接收端 接收速度 却很慢的情况下,为了保证数据不丢失,显然需要进行流量控制, 协调好

通信双方的工作节奏。所谓滑动窗口,可以理解成 接收端 所能提供的缓冲区大小。TCP利用一个滑动的窗口来告诉发送端对它所发送的数据能提供多大的缓冲区。窗口大小为

字节数起始于确认序号字段指明的值(这个值是接收端正期望接收的字节)。窗口大小是一个16bit字段,因而窗口大小最大为65535字节。

检验和

检验和覆盖了整个TCP报文段:TCP首部和数据。这是一个强制性的字段,一定是由 发端 计算和存储,并由 收端 进行验证。

紧急指针

只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。

三、TCP流量控制(滑动窗口协议)

TCP流量控制主要是针对接收端的处理速度不如发送端发送速度快的问题,消除发送方使接收方缓存溢出的可能性。

TCP流量控制主要使用滑动窗口协议,滑动窗口是 接受数据端 使用的窗口大小,用来告诉 发送端/接收端 的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量

控制的目的。这个窗口大小就是我们一次传输几个数据。对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有 落在发送窗口内的帧 才允许被发送;

同时接收方也维持着一个接收窗口,只有 落在接收窗口内的帧 才允许接收。这样通过调整发送方窗口和接收方窗口的大小可以实现流量控制。

  我们可以通过下图来分析:

1、发送方 接收到了对方发来的报文 ack = 33, win = 10,知道对方收到了 33 号前的数据,现在期望接收 [33, 43) 号数据。那我们开始发送[33, 43) 号的数据。

2、[33, 43) 号的数据你是已经发送了,但 接收方 并没有接收到[36,37]数据。所以接收方发送回对报文段 A 的确认:ack = 35, win = 10。

3、发送方收到了 ack = 35, win = 10,对方期望接收 [35, 45) 号数据。那么发送方在发送[35, 45) 。

这里面需要思考一个问题?

第一步发送了[33, 43),如果这次发送[35, 45),那中间重叠部分不是发送了两次,所以这里要思考: 是全部重新发送还是只发送接收端没有收到的数据,如果全部发送那么重复

发送的数据接收端怎么处理。这个下面快速重传会讲。

4、接收方接收到了报文段 [35, 41),接收方发送:ack = 41, win = 10. (这是一个累积确认)

5、发送方收到了 ack = 41, win = 10,对方期望接收 [41, 51) 号数据。

6、.......

这样一直传输数据,直到数据发送完成。这么一来就保证数据数据的可靠性,因为如果某数据没有获取到,那么ack永远不会跳过它。

这里也要思考一个问题,如果某一数据一直没有获取到,总不能一直这样堵塞在这里吧, 这里就要讲解:有关堵塞的解决方法。

四、TCP拥塞控制

流量控制:是通过接收方来控制流量的一种方式;

拥塞控制:是通过发送方来控制流量的一种方式。

TCP发送方可能因为IP网络的拥塞而被遏制,TCP拥塞控制就是为了解决这个问题(注意和TCP流量控制的区别)。

TCP拥塞控制的几种方法:慢启动,拥塞避免,快重传和快恢复

这里先理解一个概念: 拥塞窗口

拥塞窗口:发送方维持一个叫做拥塞窗口 cwnd的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化。

发送方的让自己的发送窗口=min(cwnd,接受端接收窗口大小)。说明: 发送方 取拥塞窗口与滑动窗口的最小值,作为发送的上限。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少

注入到网络中的分组数。

  下面将讨论 拥塞窗口cwnd 的大小是怎么变化的。

1、慢启动

TCP在连接过程的三次握手完成后,开始传数据,并不是一开始向网络通道中发送大量的数据包。因为假如网络出现问题,很多这样的大包会积攒在路由器上,很容易导致网

络中路由器缓存空间耗尽,从而发生拥塞。因此现在的TCP协议规定了,新建立的连接不能够一开始就发送大尺寸的数据包,而只能从一个小尺寸的包开始发送,在发送和数据被

对方确认的过程中去计算对方的接收速度,来逐步增加每次发送的数据量(最后到达一个稳定的值,进入高速传输阶段。相应的,慢启动过程中,TCP通道处在低速传输阶段),

以避免上述现象的发生。这个策略就是慢启动。

  画个简单的图从原理上粗略描述一下

我们思考一个慢启动引起的性能问题?

在海量用户高并发访问的大型网站后台,有一些基本的系统维护需求。比如迁移海量小文件,就是从一些机器拷贝海量小碎文件到另一些机器,来完成一些系统维护的基本需求。

  慢启动为什么会对拷贝海量小文件的需求造成重大性能损失?

举个简单的例子,我们对每个文件都采用独立的TCP连接来传输(循环使用scp拷贝就是这个例子的实际场景,很常见的用法)。那么工作过程应该是,每传输一个文件建立一个

连接,然后连接处于慢启动阶段,传输小文件,每个小文件几乎都处于独立连接的慢启动阶段被传输,这样传输过程所用的TCP包的总量就会增多。更细致的说一说这个事,如果在

慢启动过程中传输一个小文件,我们可能需要2至3个小包,而在一个已经完成慢启动的TCP通道中(TCP通道已进入在高速传输阶段),我们传输这个文件可能只需要1个大包。

  网络拷贝文件的时间基本上全部消耗都在网络传输的过程中(发数据过去等对端ACK,ACK确认归来继续再发,这样的数据来回交互相比较本机的文件读写非常耗时间),撇开三次握手和四次握手那些包,如果文件的数量足够大,这个总时间就会被放大到需求难以忍受的地步。

  因此,在迁移海量小文件的需求下,我们不能使用“对每个文件都采用独立的TCP连接来传输(循环使用scp拷贝)“这样的策略,它会使每个文件的传输都处于在一个独立TCP的慢启动阶段。

  如何避免慢启动,进而提升性能?

很简单,尽量把大量小文件放在一个TCP连接中排队传输。起初的一两个文件处于慢启动过程传输,后续的文件传输全部处于高速通道中传输,用这样的方式来减少发包的数

目,进而降低时间消耗。同样,实际上这种传输策略带来的性能提升的功劳,不仅仅归于避免慢启动,事实上也避免了大量的3次握手和四次握手,这个对海量小文件传输的性能消耗也非常致命。

2、拥塞避免

  先补充下: 慢启动中拥塞窗口的cwnd值,开始是1,接下开是指数型增涨的。1、2、4、8、16.....这样涨太快了吧。那么就有了堵塞避免。

cwnd不能一直这样无限增长下去,一定需要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,一旦cwnd>=ssthresh(大多数TCP的实现,通常大小都是65536 (2的16次方)),慢启动过程结束,拥塞避免阶段开始;

  拥塞避免:cwnd的值不再指数级往上升,开始加法增加。此时当窗口中所有的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT开始线性增加,这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。(它逻辑很简单就是到一定值后,cwnd不在是指数增长,而是+1增长。这样显然慢多了)。

  非ECN环境下的拥塞判断,发送方RTO超时,重传了一个报文段,它的逻辑如下:

1)把ssthresh降低为cwnd值的一半。

2)把cwnd重新设置为1。

3)重新进入慢启动过程。

  上面的图还是蛮好理解的。

3、快速重传

TCP要保证所有的数据包都可以到达,所以,必需要有重传机制。

  注意:  接收端给发送端的Ack确认只会确认最后一个连续的包,比如,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到)

此时的TCP会怎么办?我们要知道,因为正如前面所说的,SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端

就以为之前的都收到了。

3.1)超时重传机制

一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 回 4——意味着3和4都收到了。

  但是,这种方式会有比较严重的问题,那就是因为要死等3,所以会导致4和5即便已经收到了,而发送方也完全不知道发生了什么事,因为没有收到Ack,所以,发送方可能会

悲观地认为也丢了,所以有可能也会导致4和5的重传。

  对此有两种选择:

  ① 一种是仅重传timeout的包。也就是第3份数据。

  ② 另一种是重传timeout后所有的数据,也就是第3,4,5这三份数据。

  这两种方式有好也有不好。

  第一种会节省带宽,但是慢,

  第二种会快一点,但是会浪费带宽,也可能会有无用功。

  但总体来说都不好。因为都在等timeout,timeout可能会很长。

3.2)快速重传机制

  于是,TCP引入了一种叫Fast Retransmit的算法,不以时间驱动,而以数据驱动重传。也就是说,如果,包没有连续到达,就ack最后那个可能被丢了的包,如果发送方连续收到3次相同的ack,就重传。Fast Retransmit的好处是不用等timeout了再重传,而是只是三次相同的ack就重传。

  比如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2   因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。示意图如下

  Fast Retransmit只解决了一个问题,就是timeout的问题,它依然面临一个艰难的选择,就是重转之前的一个还是重装所有的问题。对于上面的示例来说,是重传#2呢还是重传

#2,#3,#4,#5呢?因为发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的呢。这样,发送端很有可能要重传从#2到

#20的这堆数据(这就是某些TCP的实际的实现)。可见,这是一把双刃剑。

  总结: 不管是超时重传,还是快速重传,确实能保证数据的可靠性,但它无法解决的问题就是:比如发送端发了1、2、3、4、5,而接收端收到了1、3、4、5,那么这个时候它发送的ack是2。那么发送端发送的是重传#2呢还是重传#2,#3,#4,#5的问题。如果在发送#2,#3,#4,#5,本身资源是一种浪费,因为接受方#3,#4,#5已经缓存下来,只需#2,所以在发一遍是无意义的。

【解读】TCP协议的更多相关文章

  1. 采用TCP协议实现PIC18F97J60 ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). TCP/IP Stac ...

  2. TCP协议疑难杂症全景解析

    说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的3).针对对象:对TCP已经有了全面了解的人. ...

  3. 理论经典:TCP协议的3次握手与4次挥手过程详解

    1.前言 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提供一种面向连接的.可靠的字节流服务. 面向连接意味着两个使用TCP的应用(通常是一个客户和一 ...

  4. 【转载】TCP协议疑难杂症全景解析

    说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的3).针对对象:对TCP已经有了全面了解的人. ...

  5. 访问了一次百度网页,你都经历了什么?https及tcp协议揭秘

    打开https://www.baidu.com/ 网页一个简单的动作,都经历了什么?你想探究内部的原理吗?那我们一起去探索吧 1.准备工作 安装好wireshark.Wireshark(前称Ether ...

  6. 【转载】TCP协议要点和难点全解

    说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面 2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的 3).针对对象:对TCP已经有了全面了解的 ...

  7. TCP协议要点和难点全解

    转载自http://www.cnblogs.com/leetieniu2014/p/5771324.html TCP协议要点和难点全解 说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方 ...

  8. 传输层:TCP 协议

    传输层:TCP 协议 一.概述 TCP 和 UDP 处在同一层——运输层,但是它们有很多的不同.TCP 是 TCP/IP 系列协议中最复杂的部分,它具有以下特点: (1) TCP 提供 可靠的 数据传 ...

  9. 计算机网络--TCP协议深入理解

    在近期学习计算机网络的过程中,由于知识点过于零散,琐碎,从而学习起来痛苦不堪,此贴只是总结了基于传输层的TCP协议相关的知识细节,并加入一点自己的理解,并无创新,若有理解不当之处,敬请提出,感谢! 首 ...

随机推荐

  1. Java IO(十六)InputStreamReader 和 InputStreamWriter

    Java IO(十六)InputStreamReader 和 InputStreamWriter 一.介绍 InputStreamReader 和 OutputStreamWriter 是字节流通向字 ...

  2. Netty学习笔记(二) - ChannelPipeline和ChannelHandler

    ChannelPipeline 和 ChannelHandler 是 Netty 重要的组件之一,通过这篇文章,重点了解这些组件是如何驱动数据流动和处理的. 一.ChannelHandler 在上一篇 ...

  3. ThreadLocal Thread ThreadLocalMap 之间的关系

    ThreadLocal :每个线程通过此对象都会返回各自的值,互不干扰,这是因为每个线程都存着自己的一份副本.需要注意的是线程结束后,它所保存的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用) ...

  4. Python 为什么没有 main 函数?为什么我不推荐写 main 函数?

    毫无疑问 Python 中没有所谓的 main 入口函数,但是网上经常看到一些文章提"Python 的 main 函数"."建议写 main 函数"-- 有些人 ...

  5. Java实现 LeetCode 999 车的可用捕获量(简单搜索)

    999. 车的可用捕获量 在一个 8 x 8 的棋盘上,有一个白色车(rook).也可能有空方块,白色的象(bishop)和黑色的卒(pawn).它们分别以字符 "R"," ...

  6. Java实现 LeetCode 404 左叶子之和

    404. 左叶子之和 计算给定二叉树的所有左叶子之和. 示例: 3 / \ 9 20 / \ 15 7 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24 /** * Definiti ...

  7. Java实现 LeetCode 349 两个数组的交集

    349. 两个数组的交集 给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2] 示例 2: 输入: num ...

  8. java算法集训代码填空题练习2

    1 连续数的公倍数 为什么1小时有60分钟,而不是100分钟呢?这是历史上的习惯导致. 但也并非纯粹的偶然:60是个优秀的数字,它的因子比较多. 事实上,它是1至6的每个数字的倍数.即1,2,3,4, ...

  9. Java实现第九届蓝桥杯等腰三角形

    等腰三角形 题目描述 本题目要求你在控制台输出一个由数字组成的等腰三角形. 具体的步骤是: 1. 先用1,2,3,...的自然数拼一个足够长的串 2. 用这个串填充三角形的三条边.从上方顶点开始,逆时 ...

  10. Linux 权限管理-ACL权限

    ACL权限是为了在现有的所有者.所属组.其他人不够使用的情况下使用的,使用它必须保证文件所在的分区支持ACL df -h:查看系统所有分区信息 dumpe2fs -h /dev/vda1,可以查看分区 ...