前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TCP协议详解

TCP协议详解

作者头像
kinnylee
发布2020-10-15 10:08:53
9800
发布2020-10-15 10:08:53
举报
文章被收录于专栏:kinnylee钻研技术

前言

小到基于应用层做网络开发,大到生活中无处不在的网络。我们在享受这个便利的时候,没有人会关心它如此牢固的底层基石是如何搭建的。而这些基石中很重要的一环就是tcp协议。翻看一下“三次握手”和“四次挥手”,本以为这就是tcp了,其实不然。它仅仅解决了连接和关闭的问题,传输的问题才是tcp协议更重要,更难,更复杂的问题。回头看tcp协议的原理,会发现它为了承诺上层数据传输的“可靠”,不知要应对多少网络中复杂多变的情况。简单直白列举一下:

  • 怎么保证数据都是可靠呢?---连接确认!关闭确认!收到数据确认!各种确认!!
  • 因为网络或其他原因,对方收不到数据怎么办?--超时重试
  • 网络情况千变万化,超时时间怎么确定?--根据RTT动态计算
  • 反反复复,不厌其烦的重试,导致网络拥塞怎么办?---慢启动,拥塞避免,快速重传,快速恢复
  • 发送速度和接收速度不匹配怎么办?--滑动窗口
  • 滑动窗口滑的过程中,他一直告诉我处理不过来了,不让传数据了怎么办?--ZWP
  • 滑动窗口滑的过程中,他处理得慢,就理所当然的每次让我发很少的数据,导致网络利用率很低怎么办?---Nagle

其中任何一个小环节,都凝聚了无数的算法,我们没有能力理解各个算法的实现,但是需要了解下tcp实现者的思路历程。

梳理完所有内容,大概可以知道:

tcp提供哪些机制保证了数据传输的可靠性?

tcp连接的“三次握手”和关闭的“四次挥手”流程是怎么样的?

tcp连接和关闭过程中,状态是如何变化的?

tcp头部有哪些字段,分别用来做什么的?

tcp的滑动窗口协议是什么?

超时重传的机制是什么?

如何避免传输拥塞?

一. 概述

1. tcp连接的特点

  • 提供面向连接的,可靠的字节流服务
  • 为上层应用层提供服务,不关心具体传输的内容是什么,也不知道是二进制流,还是ascii字符。

2. tcp的可靠性如何保证

  • 分块传送:数据被分割成最合适的数据块(UDP的数据报长度不变)
  • 等待确认:通过定时器等待接收端发送确认请求,收不到确认则重发
  • 确认回复:收到确认后发送确认回复(不是立即发送,通常推迟几分之一秒)
  • 数据校验:保持首部和数据的校验和,检测数据传输过程有无变化
  • 乱序排序:接收端能重排序数据,以正确的顺序交给应用端
  • 重复丢弃:接收端能丢弃重复的数据包
  • 流量缓冲:两端有固定大小的缓冲区(滑动窗口),防止速度不匹配丢数据

3. tcp的首部格式

3.1 宏观位置

  • 从应用层->传输层->网络层->链路层,每经过一次都会在报文中增加相应的首部。参考之前的文章http协议
  • TCP数据被封装在IP数据报中

3.2 首部格式

  • tcp首部数据通常包含20个字节(不包括任选字段)
  • 第1-2两个字节:源端口号
  • 第3-4两个字节:目的端口号 源端口号+ip首部中的源ip地址+目的端口号+ip首部中的目的ip地址,唯一的确定了一个tcp连接。对应编码级别的socket。
  • 第5-8四个字节:32位序号。tcp提供全双工服务,两端都有各自的序号。编号:解决网络包乱序的问题 序号如何生成:不能是固定写死的,否则断网重连时序号重复使用会乱套。tcp基于时钟生成一个序号,每4微秒加一,到2^32-1时又从0开始
  • 第9-12四个字节:32位确认序列号。上次成功收到数据字节序号加1,ack为1才有效。确认号:解决丢包的问题
  • 第13位字节:首部长度。因为任选字段长度可变
  • 后面6bite:保留
  • 随后6bite:标识位。控制各种状态
  • 第15-16两个字节:窗口大小。接收端期望接收的字节数。解决流量控制的问题
  • 第17-18两个字节:校验和。由发送端计算和存储,由接收端校验。解决数据正确性问题
  • 第19-20两个字节:紧急指针

3.3 标识位说明

  • URG:为1时,表示紧急指针有效
  • ACK:确认标识,连接建立成功后,总为1。为1时确认号有效
  • PSH:接收方应尽快把这个报文交给应用层
  • RST:复位标识,重建连接
  • SYN:建立新连接时,该位为0
  • FIN:关闭连接标识

3.4 tcp选项格式

  • 每个选项开始是1字节kind字段,说明选项的类型
  • kind为0和1的选项,只占一个字节
  • 其他kind后有一字节len,表示该选项总长度(包括kind和len)
  • kind为11,12,13表示tcp事务

3.5 MSS 最长报文大小

  • 最常见的可选字段
  • MSS只能出现在SYN时传过来(第一次握手和第二次握手时)
  • 指明本端能接收的最大长度的报文段
  • 建立连接时,双方都要发送MSS
  • 如果不发送,默认为536字节

二. 连接的建立与释放

1. 连接建立的“三次握手”

1.1 三次握手流程

  • 客户端发送SYN,表明要向服务器建立连接。同时带上序列号ISN
  • 服务器返回ACK(序号为客户端序列号+1)作为确认。同时发送SYN作为应答(SYN的序列号为服务端唯一的序号)
  • 客户端发送ACK确认收到回复(序列号为服务端序列号+1)

1.2 为什么是三次握手

  • tcp连接是全双工的,数据在两个方向上能同时传递。
  • 所以要确保双方,同时能发数据和收数据
  • 第一次握手:证明了发送方能发数据
  • 第二次握手:ack确保了接收方能收数据,syn确保了接收方能发数据
  • 第三次握手:确保了发送方能收数据
  • 实际上是四个维度的信息交换,不过中间两步合并为一次握手了。
  • 四次握手浪费,两次握手不能保证“双方同时具备收发功能”

2. 连接关闭的“四次挥手”

2.1 为什么是四次挥手

  • 因为tcp连接是全双工的,数据在两个方向上能同时传递。
  • 同时tcp支持半关闭(发送一方结束发送还能接收数据的功能)。
  • 因此每个方向都要单独关闭,且收到关系通知需要发送确认回复

2.2 为什么要支持半关闭

  • 客户端需要通知服务端,它的数据已经传输完毕
  • 同时仍要接收来自服务端的数据
  • 使用半关闭的单连接效率要比使用两个tcp连接更好

2.3 四次握手流程

  • 主动关闭的一方发送FIN,表示要单方面关闭数据的传输
  • 服务端收到FIN后,发送一个ACK作为确认(序列号为收到的序列号+1)
  • 等服务器数据传输完毕,也发送一个FIN标识,表示关闭这个方向的数据传输
  • 客户端回复ACK以确认回复

3. 连接和关闭对应的状态

3.1 状态说明

  • 服务端等待客户端连接时,处于Listen监听状态
  • 客户端主动打开请求,发送SYN时处于SYN_SENT发送状态
  • 客户端收到syn和ack,并回复ack时,处与Established状态等待发送报文
  • 服务端收到ack确认后,也处于Established状态等待发送报文
  • 客户端发送fin后,处于fin_wait_1状态
  • 服务端收到fin并发送ack时,处于close_wait状态
  • 客户端收到ack确认后,处于fin_wait_2状态
  • 服务端发送fin后,处于last_ack状态
  • 客户端收到fin后发送ack,处于time_wait状态
  • 服务端收到ack后,处于closed状态

3.2 time_wait状态

  • 也称为2MSL等待状态,MSL=Maximum Segment LifetIme,报文段最大生存时间,根据不同的tcp实现自行设定。常用值为30s,1min,2min。linux一般为30s。
  • 主动关闭的一方发送最后一个ack所处的状态
  • 这个状态必须维持2MSL等待时间
3.2.1 为什么需要这么做?
  • 设想一个场景,最后这个ack丢失了,接收方没有收到
  • 这时候接收方会重新发送fin给发送方
  • 这个等待时间就是为了防止这种情况发生,让发送方重新发送ack
  • 总结:预留足够的时间给接收端收ack。同时保证,这个连接不会和后续的连接乱套(有些路由器会缓存数据包)
3.2.2 这么做的后果?
  • 在这2MSL等待时间内,该连接(socket,ip+port)将不能被使用
  • 很多时候linux上报too many open files,说端口不够用了,就需要检查一些代码里面是不是创建大量的socket连接,而这些socket连接并不是关闭后就立马释放的
  • 客户端连接服务器的时候,一般不指定客户端的端口。因为客户端关闭然后立马启动,按照理论来说是会提示端口被占用。同样的道理,主动关闭服务器,2MSL时间内立马启动是会报端口被占用的错误
  • 多并发的短连接情况下,会出现大量的Time_wait状态。这两个参数可以解决问题,但是它违背了tcp协议,是有风险的。参数为:tcp_tw_reuse和tcp_tw_recycle
  • 如果是服务端开发,可设置keep-alive,让客户端主动关闭连接解决这个问题

4. 复位报文段

一个报文段从源地址发往目的地址,只要出现错误,都会发出复位的报文段,首部字段的RST是用于“复位”的。这些错误包括以下情况

  • 端口没有在监听
  • 异常中止:通过发送RST而不是fin来中止连接

5. 同时打开

  • 两个应用程序同时执行主动打开,称为“同时打开“
  • 这种情况极少发生
  • 两端同时发送SYN,同时进入SYN_SENT状态
  • 打开一条连接而不是两条
  • 要进行四次报文交换过程,“四次握手”

6. 同时关闭

  • 双方同时执行主动关闭
  • 进行四次报文交换
  • 状态和正常关闭不一样

7. 服务器对于并发请求的处理

  • 正等待连接的一端有一个固定长度的队列(长度叫做“积压值”,大多数情况长度为5)
  • 该队列中的连接为:已经完成了三次握手,但还没有被应用层接收(应用层需要等待最后一个ack收到后才知道这个连接)
  • 应用层接收请求的连接,将从该队列中移除
  • 当新的请求到来时,先判断队列情况来决定是否接收这个连接
  • 积压值的含义:tcp监听的端点已经被tcp接收,但是等待应用层接收的最大值。与系统允许的最大连接数,服务器接收的最大并发数无关

三. 数据的传输

1. tcp传输的数据分类

  • 成块数据传输:量大,报文段常常满
  • 交互数据传输:量小,报文段为微小分组,大量微小分组,在广域网传输会增加拥堵的出现
  • tcp处理的数据包括两类,有不同的特点,需要不同的传输技术

2. 交互数据的传输技术

2.1 经受时延的确认

  • 概念:tcp收到数据时,并不立马发送ack确认,而是稍后发送
  • 目的:将ack与需要沿该方向发送的数据一起发送,以减少开销
  • 特点:接收方不必确认每一个收到的分组,ACk是累计的,它表示接收方已经正确收到了一直到确认序号-1的所有字节
  • 延时时间:绝大多数为200ms。不能超过500ms

2.2 Nagle算法

  • 解决什么问题:微小分组导致在广域网出现的拥堵问题
  • 核心:减少了通过广域网传输的小分组数目
  • 原理:要求一个tcp连接上最多只能有一个未被确认的未完成的分组,该分组的确认到达之前,不能发送其他分组。tcp收集这些分组,确认到来之前以一个分组的形式发出去
  • 优点:自适应。确认到达的快,数据发送越快。确认慢,发送更少的组。
  • 使用注意:局域网很少使用该算法。且有些特殊场景需要禁用该算法

3. 成块数据的传输

  • 主要使用滑动窗口协议

四. 滑动窗口协议

1. 概述

  • 解决了什么问题:发送方和接收方速率不匹配时,保证可靠传输和包乱序的问题
  • 机制:接收方根据目前缓冲区大小,通知发送方目前能接收的最大值。发送方根据接收方的处理能力来发送数据。通过这种协调机制,防止接收端处理不过来。
  • 窗口大小:接收方发给发送端的这个值称为窗口大小

2. tcp缓冲区的数据结构

  • 接收端:
    • LastByteRead: 缓冲区读取到的位置
    • NextByteExpected:收到的连续包的最后一个位置
    • LastByteRcvd:收到的包的最后一个位置
    • 中间空白区:数据没有到达
  • 发送端:
    • LastByteAcked: 被接收端ack的位置,表示成功发送确认
    • LastByteSent:发出去了,还没有收到成功确认的Ack
    • LastByteWritten:上层应用正在写的地方

3. 滑动窗口示意图

3.1 初始时示意图

  • 黑框表示滑动窗口
  • #1表示收到ack确认的数据
  • #2表示还没收到ack的数据
  • #3表示在窗口中还没有发出的(接收方还有空间)
  • #4窗口以外的数据(接收方没空间)

3.2 滑动过程示意图

  • 收到36的ack,并发出46-51的字节

4. 拥塞窗口

  • 解决什么问题:发送方发送速度过快,导致中转路由器拥堵的问题
  • 机制:发送方增加一个拥塞窗口(cwnd),每次受到ack,窗口值加1。发送时,取拥塞窗口和接收方发来的窗口大小取最小值发送
  • 起到发送方流量控制的作用

5. 滑动窗口会引发的问题

5.1 零窗口

  • 如何发生: 接收端处理速度慢,发送端发送速度快。窗口大小慢慢被调为0
  • 如何解决:ZWP技术。发送zwp包给接收方,让接收方ack他的窗口大小。

5.2 糊涂窗口综合征

  • 如何发生:接收方太忙,取不完数据,导致发送方越来越小。最后只让发送方传几字节的数据。
  • 缺点:数据比tcp和ip头小太多,网络利用率太低。
  • 如何解决:避免对小的窗口大小做响应。
    • 发送端:前面说到的Nagle算法。
    • 接收端:窗口大小小于某个值,直接ack(0),阻止发送数据。窗口变大后再发。

五. 超时与重传

1. 概述

  • tcp提供可靠的运输层,使用的方法是确认机制。
  • 但是数据和确认都有可能丢失
  • tcp通过在发送时设置定时器解决这种问题
  • 定时器时间到了还没收到确认,就重传该数据

2. tcp管理的定时器类型

  • 重传定时器:等待收到确认
  • 坚持定时器:使窗口大小信息保持不断流动
  • 保活定时器:检测空闲连接崩溃或重启
  • 2MSL定时器:检测time_wait状态

3. 超时重传机制

3.1 背景

  • 接收端给发送端的Ack确认只会确认最后一个连续的包
  • 比如发送1,2,3,4,5共五份数据,接收端收到1,2,于是回ack3,然后收到4(还没收到3),此时tcp不会跳过3直接确认4,否则发送端以为3也收到了。这时你能想到的方法是什么呢?tcp又是怎么处理的呢?

3.1 被动等待的超时重传策略

  • 直观的方法是:接收方不做任何处理,等待发送方超时,然后重传。
    • 缺点:发送端不知道该重发3,还是重发3,4,5
  • 如果发送方如果只发送3:节省宽度,但是慢
  • 如果发送方如果发送3,4,5:快,但是浪费宽带
  • 总之,都在被动等待超时,超时可能很长。所以tcp不采用此方法

3.2 主动的快速重传机制

3.2.1 概述
  • 名称为:Fast Retransmit
  • 不以实际驱动,而以数据驱动重传
3.2.2 实现原理
  • 如果包没有送达,就一直ack最后那个可能被丢的包
  • 发送方连续收到3相同的ack,就重传。不用等待超时
  • 图中发生1,2,3,4,5数据
  • 数据1到达,发生ack2
  • 数据2因为某些原因没有送到
  • 后续收到3的时候,接收端并不是ack4,也不是等待。而是主动ack2
  • 收到4,5同理,一直主动ack2
  • 客户端收到三次ack2,就重传2
  • 2收到后,结合之前收到的3,4,5,直接ack6
3.2.3 快速重传的利弊
  • 解决了被动等待timeout的问题
  • 无法解决重传之前的一个,还是所有的问题。
  • 上面的例子中是重传2,还是重传2,3,4,5。因为并不清楚ack2是谁传回来的

3.3 SACK方法

3.3.1 概述
  • 为了解决快速重传的缺点,一种更好的SACK重传策略被提出
  • 基于快速重传,同时在tcp头里加了一个SACK的东西
  • 解决了什么问题:客户端应该发送哪些超时包的问题
3.3.2 实现原理
  • SACK记录一个数值范围,表示哪些数据收到了
  • linux2.4后默认打开该功能,之前版本需要配置tcp-sack参数
  • SACK只是一种辅助的方式,发送方不能完全依赖SACK。主要还是依赖ACK和timout
3.3.3 Duplicate SACK(D-SACK)
  • 使用SACK标识的范围,还可以知道告知发送方,有哪些数据被重复接收了
  • 可以让发送方知道:是发出去的包丢了,还是回来的ack包丢了

4. 超时时间的确定

4.1 背景

  • 路由器和网络流量均会变化
  • 所以超时时间肯定不能设置为一个固定值
  • 超时长:重发慢,效率低,性能差
  • 超时短:并没有丢就重发,导致网络拥塞,导致更多超时和更多重发
  • tcp会追踪这些变化,并相应的动态改变超时时间(RTO)

4.2 如何动态改变

  • 每次重传的时间间隔为上次的一倍,直到最大间隔为64s,称为“指数退避”
  • 首次重传到最后放弃重传的时间间隔一般为9min
  • 依赖以往的往返时间计算(RTT)动态的计算

4.3 往返时间(RTT)的计算方法

  • 并不是简单的ack时间和发送时间的差值。因为有重传,网络阻塞等各种变化的因素。
  • 而是通过采样多次数值,然后做估算
  • tcp使用的方法有:
    • 被平滑的RTT估计器
    • 被平滑的均值偏差估计器

4.4. 重传时间的具体计算

  • 计算往返时间(RTT),保存测量结果
  • 通过测量结果维护一个被平滑的RTT估计器和被平滑的均值偏差估计器
  • 根据这两个估计器计算下一次重传时间

5. 超时重传引发的问题-拥塞

5.1 为什么重传会引发拥塞

  • 当网络延迟突然增加时,tcp会重传数据
  • 但是过多的重传会导致网络负担加重,从而导致更大的延时和丢包,进入恶性循环
  • 也就是tcp的拥塞问题

5.2 解决拥塞-拥塞控制的算法

  • 慢启动:降低分组进入网络的传输速率
  • 拥塞避免:处理丢失分组的算法
  • 快速重传
  • 快速恢复

六. 其他定时器

1. 坚持定时器

1.1 坚持定时器存在的意义

  • 当窗口大小为0时,接收方会发送一个没有数据,只有窗口大小的ack
  • 但是,如果这个ack丢失了会出现什么问题?双方可能因为等待而中止连接
  • 坚持定时器周期性的向接收方查询窗口是否被增大。这些发出的报文段称为窗口探查

1.2 坚持定时器启动时机

  • 发送方被通告接收方窗口大小为0时

1.3 与超时重传的相同和不同

  • 相同:同样的重传时间间隔
  • 不同:窗口探查从不放弃发送,直到窗口被打开或者进程被关闭。而超时重传到一定时间就放弃发送

2. 保活定时器

2.1 保活定时器存在的意义

  • 当tcp上没有数据传输时,服务器如何检测到客户端是否还存活

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • tcp提供哪些机制保证了数据传输的可靠性?
      • tcp连接的“三次握手”和关闭的“四次挥手”流程是怎么样的?
        • tcp连接和关闭过程中,状态是如何变化的?
          • tcp头部有哪些字段,分别用来做什么的?
            • tcp的滑动窗口协议是什么?
              • 超时重传的机制是什么?
                • 如何避免传输拥塞?
                • 一. 概述
                  • 1. tcp连接的特点
                    • 2. tcp的可靠性如何保证
                      • 3. tcp的首部格式
                        • 3.1 宏观位置
                        • 3.2 首部格式
                        • 3.3 标识位说明
                        • 3.4 tcp选项格式
                        • 3.5 MSS 最长报文大小
                    • 二. 连接的建立与释放
                      • 1. 连接建立的“三次握手”
                        • 1.1 三次握手流程
                        • 1.2 为什么是三次握手
                      • 2. 连接关闭的“四次挥手”
                        • 2.1 为什么是四次挥手
                        • 2.2 为什么要支持半关闭
                        • 2.3 四次握手流程
                      • 3. 连接和关闭对应的状态
                        • 3.1 状态说明
                        • 3.2 time_wait状态
                      • 4. 复位报文段
                        • 5. 同时打开
                          • 6. 同时关闭
                            • 7. 服务器对于并发请求的处理
                            • 三. 数据的传输
                              • 1. tcp传输的数据分类
                                • 2. 交互数据的传输技术
                                  • 2.1 经受时延的确认
                                  • 2.2 Nagle算法
                                • 3. 成块数据的传输
                                • 四. 滑动窗口协议
                                  • 1. 概述
                                    • 2. tcp缓冲区的数据结构
                                      • 3. 滑动窗口示意图
                                        • 3.1 初始时示意图
                                        • 3.2 滑动过程示意图
                                      • 4. 拥塞窗口
                                        • 5. 滑动窗口会引发的问题
                                          • 5.1 零窗口
                                          • 5.2 糊涂窗口综合征
                                      • 五. 超时与重传
                                        • 1. 概述
                                          • 2. tcp管理的定时器类型
                                            • 3. 超时重传机制
                                              • 3.1 背景
                                              • 3.1 被动等待的超时重传策略
                                              • 3.2 主动的快速重传机制
                                              • 3.3 SACK方法
                                            • 4. 超时时间的确定
                                              • 4.1 背景
                                              • 4.2 如何动态改变
                                              • 4.3 往返时间(RTT)的计算方法
                                              • 4.4. 重传时间的具体计算
                                            • 5. 超时重传引发的问题-拥塞
                                              • 5.1 为什么重传会引发拥塞
                                              • 5.2 解决拥塞-拥塞控制的算法
                                          • 六. 其他定时器
                                            • 1. 坚持定时器
                                              • 1.1 坚持定时器存在的意义
                                              • 1.2 坚持定时器启动时机
                                              • 1.3 与超时重传的相同和不同
                                            • 2. 保活定时器
                                              • 2.1 保活定时器存在的意义
                                          • 参考
                                          相关产品与服务
                                          云服务器
                                          云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档