计网总结(传输层)

传输层在网络层提供的服务基础上,实现了端对端之间的服务

知识点

  • UDP
  • TCP
  • UDP和TCP如何实现复用和解复用
  • TCP如何实现可靠传输、流量控制、拥塞控制
  • TCP的连接建立与连接拆除,通俗来说就是三次握手、四次挥手

总的来说,能把TCP和UDP全部理清楚即可

首先先来总体概括一下TCP和UDP

  • 传输层主要的两个协议是UDP和TCP

  • UDP:User Datagram protocol,用户数据报协议,无连接服务

  • TCP:Transmission Control Protocol,传输控制协议,面向连接服务

  • 传输层的连接是指:两个应用进程在进行通信前,需不需要进行连接,连接可以确保通信的正常进行

多路复用与解复用

  • 这里的多路复用是指:应用层复用的是同一层的UDP和TCP实体,也就是多个应用会使用同一个UDP和TCP实体来进行发送分组

  • 这里的解复用是指:当TCP和UDP实体收到了网络层传递的分组时,可以准确分配到各个应用进程

  • 这里首先要理解,应用层都是通过Socket套接字来调用传输层服务的,一个应用进程可以有多个Socket,也就形成了一对多,当然TCP、UDP实体只有一个,其也是形成了一对多

  • socket绑定了一些连接信息,TCP和UDP的socket是不一样的,TCP的socket里面有目标IP、目标端口、自身IP、自身端口还有PID(进程ID);而UDP的socket则仅仅只有自身IP、自身端口和PID,这是因为TCP提供的是面向连接的服务,socket会去维护连接的信息,这样做的目的是让与该连接有关联的分组都交由该socket来发送给对方或者返回给上层,而UDP提供的是无连接服务,将每个分组都视为独立,独立去发送、处理每一个分组,不需要记录分组的状态(都是独立的),所以不需要去维护连接信息,直接全部交由对应应用程序处理即可

  • TCP和UDP实体里面都维护了自身的Socket表,当TCP和UDP接收到分组的时候,会对比socket表来进行分派分给应用层的应用程序,TCP主要是根据源IP地址、源端口号、目标IP地址、目标端口号,从而找到对应的socket;而UDP则只需源IP地址、源端口号即可找到对应的socket,使用对应的socket返回给上面的应用程序

  • TCP所谓的连接,其实指的就是对应的socket的连接,比如两个应用进程进行连接通信,整个通信过程都将是对应的两个socket之间,socket都绑定了对方的信息,从而记录了这条端对端连接,以后的通信都是基于该连接进行!对于接收到的属于同个应用程序,但不同连接的分组会分配到对应的scoket进行处理;但是UDP虽然也是基于两个socket进行通信,但却不会绑定对方信息,也不会去记录这条端对端连接,对于接收到的属于同个应用程序,但不同连接的分组都是交由同个socket一致处理

UDP

UDP协议称为User Datagram Protocol,也就是用户数据报,其封装之后的数据称为数据报

UDP的实现比较简单

  • UDP仅仅在原来的IP服务的不可靠服务上添加了复用和解复用、差错检测功能,依然提供的是不可靠的服务!
  • UDP是无连接的,也就是对于所有分组都视为独立的分组进行处理,不需要在通信前连接建立和通信后拆除连接上占用额外的资源
  • UDP是面向报文的,也就是说对于网络层传来的报文,只是简单将头部去除了之后就将数据部分交由应用层了;对于应用层的处理也只是简单添加了头部之后就交由网络层发送了,也就是说UDP会一次交付完整的报文,并不会进行分片、重组等操作,因此选用UDP协议的时候,要考虑应用层的报文长度,太长的话会导致网络层对其进行分片重组,降低了网络层的效率,太小的话又不能充分利用网络层的资源!
  • UDP尽最大努力去交付报文,但不会对报文负责,不保证报文一定被接收方接收到了
  • UDP支持一对一、一对多以及多对多
  • UDP不支持拥塞控制、流量控制、和提供可靠服务,其仅仅支持差错检测
  • UDP的首部开销小,仅仅只有8个字节,相比于TCP的20个字节,要占用更少的空间

UDP的报文格式

UDP的报文格式也分为首部和数据部分

  • 首部:占用八个字节,可以看到UDP并没有IP地址,因为IP地址是在IP层上应用的!在IP层进行封装和解封装的!解封装之后是会传上来使用的,而传输层使用的则是端口号,关注的是端口号!

    • 源端口:源端口号,应用层用来响应的时候会使用到这个字段
    • 目的端口:目的端口号
    • 长度:UDP报文长度,可以通过此来计算出数据部分
    • 校验和:用来检验首部是否出现错误,如果出现错误,报文就代表出错了
  • 数据部分:也就是应用层传过来的报文

对于UDP报文的处理细节

  • 如果对比UDP实例中的scoket表,发送目的端口号不存在,则会视为端口不可达,发送对应类型的ICMP报文,进行差错报告

对于UDP报文的校验

  • 当接收到一个UDP数据报的时候,会利用其校验和进行检验,这里要注意,校验和的形成不仅仅与首部相关,还会与数据部分相关!因此也可以用来检测数据部分是否出现了问题!而IP仅仅只是检验了首部

  • 校验和字段初始化方式:12字节的伪首部 + UDP首部 + UDP数据部分,计算方式为,首先把16位全0填充进校验和字段,然后将各个部分拆分成一段段16位字,伪首部占12字节、UDP首部占8字节,肯定可以刚好拆分的,但如果UDP数据部分不是偶数个字节,则会进行填充0,填充到1个字节的0,这些填充0是不会被发送的,同理伪首部也不会发送的!!,每段16位字进行相加之后然后进行反码得到16位的校验和(超出的高位作为低位的进位!)

  • 关于伪首部:伪首部虽然不会参与运算,但却是可以计算得出,伪首部的组成有5个部分

    • 源IP地址:4个字节
    • 目标IP地址:4个字节
    • 8位全0+8位协议代码:2个字节,协议代码是UDP或者TCP,如果是UDP则为17(0X11)!
    • UDP用户数据报长度:2个字节
  • 检验和的使用:当接收方接收到UDP用户数据报时,计算出伪首部,然后伪首部、UDP数据报头部和UDP数据部分同样分成一段段16位字,然后与UDP数据报头部中的校验和字段进行二进制相加,其结果最后为全1,则代表无误,否则代表发生错误了,因为checksum保存的是反码!

  • 当checksum校验出错的时候,可能会选择直接抛弃该分组,也可能会将错误信息上交给上层的应用层

  • 检错方式虽然能力不强,但处理简单,处理起来快!

TCP

TCP协议全称为Transmisson Controler Protocol,称为传输控制协议

TCP的特点

  • 面向连接:两个应用进程在通信前,必须要建立TCP连接,这里的连接也就是上面我们提到的socket连接,绑定了对方的信息!
  • 提供可靠的交付服务,通过TCP连接发送的数据,不出现乱序、丢失、重复等问题
  • 有拥塞控制和流量控制功能
  • 采用全双工的通信,允许发送和接收动作同时发生,具体的实现是,TCP的两端设定了发送缓存和接收缓存,应用层将要发送的数据放在TCP的发送缓存中就可以去做自己的事情了,而TCP会在适当的时机将发送缓存中的数据进行发送;同理,TCP把收到的数据放在接收缓存中,在适当的时机会传给应用层
  • 面向字节流,TCP会将缓存中的数据视为一连串的字节形成的无结构的字节流,发送的时候也是以这种字节流的形式发送

TCP的功能,在网络层提供的服务上,还添加了以下功能

  • 多路复用、解复用
  • 可靠传输
  • 拥塞控制
  • 流量控制

可靠传输的实现

RDT

IP层仅仅提供最大努力的服务,并不可靠,而TCP为了让其变得可靠,那么就要进行增强处理

因此TCP有三种协议来实现可靠传输

  • 停止等待协议(stop-and-waiting)
  • 回退N协议(go-back-N)
  • 选择重传协议(sr)

不过在认识这三种协议之前,我们必须要认识一下RDT的一个发展过程,RDT其实就是可靠数据传输,并不是一个协议,而是一种概念,就是当依赖的下层是不可靠的时候,上层怎么去提供可靠的服务

网络上的分组传输过程中的两个问题

  • 分组出错
  • 分组丢失

RDT1.0

前提条件:网络很好,下层接收到的分组没有出错、也没有丢失,RDT1.0直接对分组进行解封装然后传递给上层即可

待解决的问题:啥也没解决,搁着搁哪的

RDT2.0

前提条件:网络差了一点,分组出现出错了,但没有丢失,使用了ACK/NAK确认码来实现了检错重传机制

  • 发送方发送分组,并做好分组备份
  • 接收方接收到了分组,并去进行校验,如果没问题,发送ACK确认码,如果出错了,发送NAK错误码
  • 发送方收到了接收方的响应,如果是ACK确认码,则发送下一个分组;如果收到的是NAK错误码,则进行重传

出现了新的问题,ACK、NAK丢失、出错了怎么办,甚至NAK变成了ACK。。。

RDT3.0

对于RDT2.0的问题进行优化,首先,肯定不能再加多一层对确认码检错的功能!因为这是一个无底洞!!

  • 去除了NAK,因为会出现变化问题,因此不需要NAK了,只发送ACK确认码
  • 使用编号,给每个分组和ACK带上编号,ACK上的编号代表接收方确认收到了什么分组,比如ACK0,代表0号分组确认接收;可以发送1号分组了,假如发送1号分组,收到了ACK0,代表出错了,只确认了0号分组,并没有确认1号分组,因此需要重发1号分组
  • 总的来说就是使用了编号的技术,解决了确认码出现错误的问题,并且解决了重复接收问题,接收方可以通过对比编号来看之前是不是已经收到过该分组

但还有丢失的问题没有解决!

RDT4.0

  • 在RDT3.0的基础上,添加了定时重发功能,当发送一个分组就会开启定时器,如果在一定时间内没有接收到确认码则会进行重发

RDT整个发展历程就是这样

停止等待协议

stop-waiting协议,底层的概念其实就是RDT4.0,过程如下

  • 发送方发送带有编号的分组,并开启定时器
  • 接收方响应带有编号的ACK
  • 发送方接收到发送方对应的ACK,根据ACK的编号去发送下一个分组!因此ACK的编号其实代表的含义是接收方想要的分组
  • 发送方发送了分组就会进行等待,等待ACK的到来才会发送下一个分组,因此称为停止等待协议
  • 当超过了定时器时,就会进行重发分组
  • 如果超时了之后,发送方进行重发了,并且收到了迟到的ACK确认,那么也会视为确认,然后发送下一个分组,然后接收方根据重发的分组会进行抛弃,并且再次发送ACK,发送方收到了已经确认过的ACK,直接抛弃!!

要注意的以下几点

  • 发送方每发送一个分组前都要做好备份,并且要记录确认的ACK
  • 分组和确认的ACK都要进行编号,ACK的编号代表了接收方想要发送方发送的分组
  • 定时器的超时时间一般设置成比平均往返时间要长一些

出现的问题

  • 每次只发送一个分组,然后等待分组的确认才发送下一个分组,导致信道的利用率会很低,发送方的信号在发送了分组之后就空闲了!

信号利用率计算: T D / ( T D + R T T + T A ) , T D 代 表 发 送 的 分 组 占 用 的 时 间 , 分 母 代 表 往 返 时 间 , R T T 是 路 由 的 时 间 , 而 T A 是 接 收 方 发 送 确 认 码 的 时 间 T_D/(T_D + RTT + T_A),T_D代表发送的分组占用的时间,分母代表往返时间,RTT是路由的时间,而T_A是接收方发送确认码的时间 TD/(TD+RTT+TA)TDRTTTA

为了提高分组的效率,出现了连续ARQ协议

连续ARQ协议

针对一个分组发送和确认的对信道的低效率利用,采用了流水线来进行优化,也就是ARQ,使用了发送窗口

流水线:发送方发送了一个分组之后,不需要等待分组确认了之后才发送下一个分组,直接可以发送下一个分组,也就是不需要等待确认即可发送下一个分组

  • 连续的ARQ协议采用滑动窗口来实现,滑动窗口分为发送滑动窗口和接收滑动窗口
  • 发送窗口:只有位于发送窗口里面的分组才能进行发送,当发送完了一个分组后沿可能会移动;但只有最前沿的分组确认了之后,前沿才能进行移动,发送窗口的长度会有限制,这也是为什么发送完了一个分组之后,后沿不一定会移动,需要等待前沿移动才能移动
  • 接收窗口:位于接收窗口里面的分组代表正在等待确认,当接收到接收窗口里面的分组会发送对应的ACK,当发送了一个分组的确认ACK之后,后沿可能会移动;但只有最前沿的分组的ACK发送之后,接收分组的最前沿才会移动,接收窗口有限制长度,这也是为什么发送了ACK之后,后沿不一定会移动

各种可靠传输的协议,其实都是基于ARQ的滑动窗口来实现的

  • 停止等待协议:发送窗口的长度为1,接收窗口的长度也为1
  • 回退N协议:发送窗口的长度大于1,接收窗口的长度为1
  • 选择重传协议:发送窗口的长度大于1,接收窗口的长度也大于1
GBN协议

GBN协议,go-back-n,也就是回退N

  • 发送窗口的长度大于1,接收窗口的长度为1
  • 只有接收窗口中的分组到来了,才会进行发送ACK、然后移动,会移动到下一个接收到的最大的有序编号的分组
  • 发送的ACK起到一个累计确认的作用,比如ACK3,代表3号和3号之前的分组都已经确认接收到了
  • GBN会一次性将发送窗口内的分组全部发送,并且仅仅维护最迟发送的分组的定时器!
  • 接收方在接收到分组之后不会立刻确认而是会等待一段时间,因为这是累积确认,会发送接收的有序分组中的最大编号的ACK确认,比如1、2、3、5,则是发送ACK3,因为5已经不连续了
  • 发送方收到确认ACK,会移动滑动窗口,发送后面的分组,并且会重新设置定时器
  • GBN的回退重传仅仅只会出现在超时的情况下,也就是分组丢失、或ACK确认码延迟,当超时定时器触发之后,会将窗口中已经发送但未收到确认的全部分组进行重发,然后定时器重新设置
  • 接收方会根据编号排除重复的分组,发送方接收到迟到的ACK确认也会继续移动窗口,对于重复的ACK则会抛弃,这是RDT4.0已经解决的问题
  • 对于乱序到来的分组会保存在接收缓存里
SR协议

对于GBN协议,虽然解决了信道的利用率问题,但如果在网络延迟的状态下,会导致很多确认ACK迟到,从而导致很多的超时重发,因为超时重发是重发所有已发送未确认的分组,因此会导致很多不必要重发的分组进行重发

SR协议,Selective Repeat,选择重传协议

  • 发送窗口的长度大于1,接收窗口的长度也是大于1

  • 同样是发送滑动窗口里面的分组才能进行发送,但没发送一个分组就开启对应的定时器,同时ACK不再是一个累积确认,而是单个确认!收到一个ACK就会将对应的分组进行确认并且关掉超时定时器

  • 发送窗口只有当最前沿的分组确认了之后才会进行移动!

  • 接收窗口扩大了,当收到一个分组之后会发送对应的ACK确认,然后进行移动,并且会有长度限制,最前沿的移动只有当发送了最前沿的分组的ACK才会进行移动,所以说,当接收窗口扩大到阈值的时候,就必须要等待前沿的移动才能继续带动后沿移动了!,使用了**扩大的接收窗口来缓存那些乱序的分组,只最前沿的分组来了,然后发送了确认帧,才会移动窗口从而将确认的分组转交给上层!此时接收窗口里面就缓存了那些乱序的分组,**因此接收窗口可能会滑动较大的距离,滑动到最近的一个未接收的分组,乱序到来的分组保存在接收窗口中

  • 同样只有超时会导致重发现象,并且对于超时,仅仅只会发送超时的分组,而不是所有的未确认分组

GBN协议与SR协议的区别
  • GBN协议的接收窗口长度为1,而SR协议的窗口长度为N
  • GBN协议的发送窗口仅仅只维护最后发送的分组的超时定时器,而SR给发送窗口的每个元素都维护了超时定时器
  • GBN协议和SR协议都只有超时的时候才会进行重传,但GBN超时定时器触发的重传会将发送窗口里面所有未确认的分组进行重传,而SR协议只是单独重传超时的分组
  • GBN的ACK是一个累积确认,代表了前面的分组都已经收到了,而SR协议的ACK则是一个单体确认,仅仅代表对应分组已经收到了
  • GBN的接收窗口仅仅只有一个,只会根据来到的分组最大的有序编号进行有序移动,传送给上层的分组肯定是有序的;而SR的接收窗口是多个,会发生乱序的现象,而SR对于乱序分组的处理是缓存在接收窗口中,只有当前沿的分组到来之后才会进行移动窗口,然后传递给上层!

TCP对于GBN协议与SR协议都有对应的实现!但TCP对于GBN的实现有点差异,其接收窗口的长度并不是为1,而是N,使用N的接收窗口是为了可以按顺序存放那些乱序到来的分组,,整体的接收缓存包括了接收窗口,之所以要使用接收窗口,是因为后续实现流量控制的时候,接收窗口可以用来衡量接收方目前还能接收多少分组,接收窗口会影响到发送窗口,接收窗口如果按照理论的GBN协议,只能为1,对于发送窗口是没有意义的,具体使用哪一个协议是参考TCP报文段中的section字段

TCP实现可靠传输

TCP既有GBN协议,也有SR协议的实现,只不过对于GBN的实现,接收方的窗口并不是受限于一个长度,而是N个,因为在后续的流量控制和拥塞控制,接收方窗口的长度都会对发送方窗口的长度有影响的,

GBN的实现
  • 滑动窗口是以字节为单位的,并不是以报文段为单位的
  • 确认报文段不仅会发送期望收到下一个序号的分组,还会发送接收窗口的大小,在未通信之前,是通过TCP的连接来确定接收窗口和要发送的第一个报文段的编号,发送方会根据接收方的接收窗口来设置发送窗口,接收方同时也知道了第一个报文段的编号
  • 接收方发送的ACK确认报文表示累积确认,代表该序号之前的分组都已经确认交付了,发送方接收到对应的ACK确认报文会开始滑动发送窗口,新进入发送窗口的分组可以立马发送,接收方对于乱序到来的分组会存放在接收窗口内,也会发送ACK确认,但不是确认乱序到来的分组,而是确认接收窗口最前沿的分组序号,因为接收窗口里面只有乱序到来的分组和未到来的分组,第一个分组肯定是第一个缺失的,只有最前沿的分组来了,接收窗口才可以滑动
  • 应用层将报文段发送到TCP的发送缓存即可,发送缓冲包括了发送窗口和未发送的分组;TCP将确认的分组放在TCP的接收缓存中,接收缓存包括按序到达的分组和接收窗口,接收窗口里面包括了乱序到来的分组和未到来的分组
  • GBN协议的缺点在于ACK是一个累积确认,并且一旦发生超时,发送方会将前面未确认的分组都进行重发,如果在网络拥塞的情况下,导致ACK大量迟到,发送方会重发很多不必要的分组,因此超时重传时间一定要选择好
    • TCP采用自适应算法来确定超时重传时间,但确定当前的超时重传时间首先要确定当前的平均往返延迟,TCP是采用加权运算来计算当前的平均往返延迟的,称为加权平均往返延迟,每次得到一个新的RTT样本时,就会重新计算平均往返延迟,RTT就是当前样本的往返延迟,发送方发送一个分组到接收到确认报文得时间,计算当前的平均往返延迟会根据旧的平均往返延迟(第一次就为0)和现在得到新的RTT样本进行一个加权运算,旧的往返延迟的权重为1-a,而新的RTT样本的权重为a,公式为: 新 的 R T T s = ( 1 − a ) ∗ 旧 的 R T T s + a ∗ 当 前 的 R T T 新的RTTs = (1-a)*旧的RTTs+a*当前的RTT RTTs=(1a)RTTs+aRTT,一般a为八分之一,也就是0.125,所以对于旧的往返延迟的权重为0.875,当前样本的往返延迟的权重作为0.125,可以看出来比较倾向于旧的平均往返延迟
      • a大于等于0且小于1,当a接近于0时,代表新的平均往返延迟与旧的平均往返延迟区别不大,当a接近于1时,代表平均往返延迟贴近于新样本的往返延迟
    • 确定了平均往返延迟就可以确定超时重传时间,前面提到过超时重传时间一般要比平均往返延迟大,超时重传时间为当前的平均往返延迟加上当前样本的RTT的偏差的加权平均值的四倍,偏差是指新的平均往返延迟减去样本的平均往返延迟的绝对值,当前偏差的加权平均值为上一次偏差加权平均值与当前偏差进行加权运算,上一次偏差加权的平均值的权重为0.75,当前偏差的权重为0.25,也就是四分之三与四分之一,比较倾向于上一次的偏差加权
    • 但还有一个问题,加入发生了重传,该如何确定当前接收到的确认报文段的往返延迟,是对应之前的,还是对应重传的?加入当前接收到的确认报文段是对应重传报文的,但却对应的原来报文段的确认,会导致当前样本的RTT过大,导致重传时间RTO就会偏大;同理,如果来的是原来报文段的确认,但却对应上了重传的报文段,会导致当前样本的RTT过小,导致重传时间RTO就会偏小,因此就有了Krim算法,该算法计算平均往返延迟和超时重传时间的公式不变,但对于发生了重传的报文段将不会当作样本去重新计算平均往返延迟和超时重传时间,这样计算出的时间就会比较准确
    • 使用Krim算法依然会有问题,假如网络突然拥塞了,在原先的超时重传的时间内已经不能够收到确认报文段了,那么会导致永远都不会更新超时重传时间,因为每个报文段都超时了,因此对Krim算法进行修正,当发生超时重传时,会对超时重传时间进行增大,一般增大为2倍,当不再发生报文段超时重传的时候才采用上面的计算公式
SR的实现
  • 在TCP中,选择重传协议称为Selective ACK
  • 前面已经提到过,对于GBN,在网络拥塞的情况下,如果发生ACK迟到,那么就会导致很多不必要的重发,因此使用选择重传协议去减少不必要的重发,哪个段超时对哪个进行重发
  • 大多数的实现爱是GBN

流量控制

  • 流量控制的目的:确保接收方可以来得及接收发送方发送的分组,避免由于接收缓存溢出,导致分组被丢弃,流量控制是端对端之间的策略
  • 在建立TCP通信前,接收方会告诉发送方自身的接收窗口是多大,单位为字节,发送方会根据接收窗口来调整自己的发送窗口,不一定是发送窗口等于接收窗口,还要看拥塞窗口;同时发送方会告诉接收方要发送第一个报文段的编号,便于接收窗口进行滑动
  • TCP的流量控制是基于滑动窗口来实现的,在建立的TCP连接后,接收方发送的确认报文里面会带上接收窗口还有多少空闲的空间,具体来说是rwnd字段,发送方通过确认报文中的接收窗口的空间空间会不断调整自己的发送窗口,当接收窗口的空间为0字节时,发送方就会停止发送,因为接收方的接收窗口已经满了,不能再接收了,发送方必须等待下一次ACK确认报文的到来,然后重新调整,才能继续发送
  • 流量控制出现的死锁:当发送方接收到的ACK确认报文里面的rwnd字段为0,证明发送窗口已经满了,此时就会停止发送,发送窗口也变为了0,必须等待下一次ACK确认报文的带来才会调整发送窗口,但如果后续的ACK报文出现了丢失,接收方并不会重传ACK确认报文,此时就会陷入一个死锁,接收方在等待发送方发送分组,而发送方在等待接收方的ACK确认报文,形成了一个互相等待的过程,这里要注意,此时发送方的是不会进行重传的,因为发送方知道了接收方的接收窗口没有空间了!
    • 如何解决死锁?TCP是采用持续计时器来解决的,当发送方接收到了一个零窗口值的确认报文,会开启持续计时器,当持续计时器设置的时间到期,发送方会发送零窗口探测报文段,此时接收方在确认该零窗口探测报文段会给出接收窗口值,发送方收到确认报文后,如果窗口值仍为0,则重启持续计时器,如果不为0,就继续发送剩下的报文段,从而打破了死锁的僵局

拥塞控制

  • 拥塞控制:当网络中正在路由转发的分组越来越多时,会出现网络拥塞,导致报文丢失,拥塞控制就是用来减缓网络拥塞的,是网络层面的策略,拥塞控制的目的:防止过多的数据流入网络中,避免网络中的路由器或者链路过载,不做拥塞控制最严重的情况就是网络中出现死锁,也就是整个网络因为拥塞从而停止了整个服务。。。

  • 拥塞问题不能仅靠简单地扩大设备的缓存存储空间来解决,因为拥塞问题的主要本质是整个网络系统的各个部分的不匹配、不均衡导致的,比如设备之间的发送速率与接收处理速率的不匹配,如果说要通过硬件手段去解决,必须要处理网络系统中的所有的硬件,这显然是不可能实现的,并且扩大了设备的缓存存储空间,后续当分组越来越多仍然会出现拥塞现象,治标不治本的

  • 拥塞控制首先要做到的第一点是检测拥塞,只有检测出拥塞了后续才能继续处理,一般对于拥塞检测有两种方式

    • 开环控制:将所有拥塞的情况都考虑好,网络提供服务时就尽量去减少拥塞,避免拥塞的发生
    • 闭环控制:通过反馈的机制来检测出拥塞,然后进行拥塞控制
    • 显然开环控制是很难实现出来的,所以现在采用的是闭环控制
  • TCP实现拥塞控制主要有4种算法

    • 在认识拥塞控制的4种算法前,前面流量控制已经提到过,发送窗口不仅与接收窗口有关、还会与拥塞窗口有关,发送窗口实际上取得是拥塞窗口与接收窗口的最小值,而拥塞控制采用的算法其实就是通过拥塞窗口来进行控制发送窗口的,从而减少分组的发送,其实四种算法对应着四种主机对网络状态的认识

    • 慢启动:建立完连接后,发送方主机并不知道网络状态如何,如果一开始进行大量发送数据,可能就会导致网路拥塞,因此会先进行探测,逐步去扩大拥塞窗口值,这也就是慢开始,但慢开始其实并不慢,一开始的拥塞窗口值为1,当每收到一个确认报文时,就会进行加1,也就是拥塞窗口会添加一个SMASS,也就是一段报文的最大长度,加1其实是以报文段为单位,所以每一个轮次之后,拥塞窗口值都会变为两倍,一个轮次就是指发送的分组都得到了确认,对应其实就是一个RTT,以两倍的速度增长,所以慢开始其实并不算慢

    • 拥塞避免:慢开始每个轮次之后,拥塞窗口值都会变为2倍,持续下去一定会发生网络拥塞的,因此要设定一个慢开始阈值,一旦达到拥塞窗口达到慢开始阈值之后,就进入了拥塞避免的阶段,也就是发生了轻微的拥塞,使用拥塞避免算法了,拥塞避免并没有停止拥塞窗口的增长,因为此时还没有拥塞现象嘛,主机估计可能会发生轻微拥塞而已,但增长速度肯定不会像慢开始一样以倍数增长,而是没过一个RTT就自增1,增加一个SMASS,也就是加法增大,拥塞避免并没有停止拥塞窗口的增加,但降低了增加的速率,相当于主机更加谨慎地去试探网络了

    • 超时现象:一旦出现了超时,发送方就会立即判断网络此时正在拥塞了,会立即将慢开始的阈值设定为当前拥塞窗口的一半,然后拥塞窗口设为1,重新回到慢开始的阶段

      • 为什么要设为一半呢?因为当前的拥塞窗口值会导致网络拥塞,此时已经试探出网络拥塞时的拥塞窗口值了,因此肯定需要根据当前拥塞窗口值去重新定位慢开始的阈值,重新定位发生轻微拥塞的窗口值,这过程也是一个动态调整的过程;为什么要回到慢开始的阶段?因为网络此刻已经发生拥塞了,主机必须要迅速降低分组的发送,避免网络发生严重拥塞

      • 但一旦出现超时就判断为拥塞,其实并不太合理,因为可能存在着网络并不拥塞,但分组却丢失的情况,因此有了快重传算法

    • 快重传:快重传算法是需要接收方配合的,接收方在收到分组时,都必须要立刻发送确认报文,无论该分组是乱序到来的、还是顺序到来的,乱序到来就发送前面顺序到来的分组的确认报文,不能够使用捎带技术,因此,当发送方连续接收到了三个重复的确认报文时(这里重复的确认报文不是指重发导致的,而是乱序导致的!!!),就可以马上意识到出现了分组丢失了并且3个重复的确认报文可以到来,也证明了网络并不拥塞,此时就不需要等待分组过期才进行重传,应该立刻进行重传,所以称为快重传,避免重传发生导致的误判

    • 快恢复:当收到了3个连续的ACK时,发送方知道了只是丢失了个别的报文段,但也不会完全不管,会判定为可能发生轻微拥塞了,不会进行慢启动,而是进入了拥塞避免的阶段,再进行重试探,将慢启动的阈值设为此时拥塞窗口的一半,然后将拥塞窗口直接设为当前慢启动的阈值,执行拥塞避免的算法

TCP的报文段

  • TCP虽然是面向字节流的,也就是以字节流的形式发送,但其基本单位还是报文段

  • TCP的报文段也分为两部分,首部与数据部分,一般我们研究报文段也只是看首部

    • 首部:首部的大小不是固定的,不过前20字节是固定的,然后有一个可变产孤独的可选项

      • 源端口、目的端口:各占2字节,总共4字节

      • 序号(seq):占4个字节,代表发送的字节的序号,称为报文段序号,起始序号必须在连接时确定;在通信中,序号为发送的字节的序号,而在建立连接时,序号可以理解成发送窗口和接收窗口的起始序号

      • 确认号(ack):占4个字节,代表期待接收对方下一个的字节的序号,并且该确认号起到的是一个累积确认的效果

      • 数据偏移:占4位,代表数据部分距离TCP数据起点的距离单位32位字,也就是4个字节;4位的最大值就是15,所以首部最大占60个字节空间

      • 保留:占6位,保留的字段,后续使用

      • 6个标志位

        • URG:代表紧急标志位,当该标志位为1时,代表该报文段要紧急发送

        • ACK:代表确认标志位,只有该标志位为1时,确认号ack才会有效

        • PSH:代表推送标志位,当该标志位为1时,代表该报文段需要立刻发送或者立刻向上交付,不需要等待TCP的缓存区满了之后才进行发送或者向上交付

        • RST:代表复位标志位,当该标志位为1时,代表TCP连接过程中出现了严重错误,必须释放连接,然后再重新去建立连接

        • SYN:代表同步标志位,当该标志位为1时,代表正在与对方建立连接,正在同步序号

        • FIN:代表完成标志位,当该标志位为1时,代表报文段是最后一个了,发送方的数据已经发完了,并且要求对方释放连接

      • 窗口:接收方的接收窗口,相当于接收缓冲,用于让发送方实现流量控制的,代表了发送方可以发送多少个字节

      • 校验和:与UDP的校验和字段一致,只不过采用的协议是TCP,而TCP的协议号为6

      • 紧急指针:用于与URG配合使用,当URG为1时才有意义,指出了紧急数据的末尾在报文段中的位置,并且即使窗口值为0,也可以发送紧急数据

      • 选项:长度可变,最长可以达到40字节,TCP的首部前部分的长度固定为20字节,因此TCP首部的占用的最多字节数为60字节,这也符合数据偏移字段允许的字节数目

    • 数据部分:上一层应用层传递下来的报文内容

  • 关于MSS

    • MSS是Maximun Segment Size,也就是最大的报文段大小,但起始这里并不是报文段的最大的字节占用,只是单独指报文段的数据部分的最大的字节占用
    • MSS是指代数据部分的最大的字节占用,因此如果要说整个TCP报文段的最大值是60+MSS字节,最小的则为20字节
    • 当MSS较小时,那么网络的利用率会比较低,因为TCP协议加上IP协议的头部,加起来总共有40个字节,而真实需要发送的数据大小为MSS,整体的网络利用率为 M S S / ( M S S + 20 + 20 ) MSS/(MSS + 20 + 20) MSS/(MSS+20+20),如果MSS过小,会导致网络利用率偏低
    • 当MSS很大时,虽然可以提高网络利用率,但过大的数据部分会导致IP层发生分片行为,带来额外的消耗,所以不能盲目去提高MSS,所以MSS可以提高,但要限制在IP发分片行为的范围内,因此最佳的MSS是很难确定的
    • 默认的MSS是536字节

TCP的可靠连接

  • TCP提供的是面向连接、可靠的通信服务,在通信之前必须要建立连接,也就是TCP的三次握手
  • 不过在进行握手之前,双方都需要打开传输控制模块TCB(Transaction Control Block),传输控制模块存储着连接中的一些重要信息,比如TCP连接表,当前需要发送和接收序号
  • 第一次握手:客户端确定了初始的发送序号X,并且发送连接请求,连接请求中SYN标志位为1、seq标志位为X,也就是发送窗口的第一个序号,代表是一个连接请求,seq标志位告知了对方初始的发送序号从X开始,此时客户端进入了SYN_SENT状态,代表发生了连接请求
  • 第二次握手:服务端收到了客户端的连接请求,需要进行连接确认响应,会先确定了初始的接收序号Y,也就是接收窗口的第一个序号,连接确认的响应中SYN标志为1,ACK标志位为1,ack为X+1,seq为Y,此时服务端进入了SYN_RECV状态,代表接收到了连接请求,并响应了
  • 第三次握手:客户端收到了服务端的确认连接响应,需要对服务端继续进行确认,发送的确认报文段的seq为X+1、ack为Y+1、ACK=1,此时并不会发送SYN=1,因为SYN是同步标志位,是用来通信双方进行序号同步的,此时服务端知道了客户端发送窗口的起始值,客户端也知道了服务端的接收窗口的起始值,已经完成序号同步了,此时客户端进入了established状态,代表连接建立;并且此时收到了客户端确认连接的响应,也会进入established状态,代表连接建立,并且足以,第三次握手的数据报的头部已经跟普通数据报的头部没什么大区别了。
  • 三次握手之后就完成了连接建立,可以进行通信和发送数据了
为什么是三次握手,两次行不行,四次、五次呢?
  • 两次握手的情况
    • 两次握手的情况就是客户端发送连接请求、然后服务端确认连接请求之后,就可以开始通信
    • 假如第一次握手的时,连接确认的报文出现了延迟到达,或者是连接请求的报文出现了延迟到达,并且导致了客户端重发了连接请求,服务端对于这两次连接请求都会做出连接确认响应(因为可能真的出现了连接确认的报文丢失)那么此时客户端发送的两次连接请求就有可能存在失效连接的问题,因为两次的连接请求在不丢失的情况下会有两次的连接确认,但客户端在收到了第一个连接确认后,就可以进行发送数据了(二次握手),如果第二个连接确认在客户端发送完数据并且已经释放了连接的时候到达,客户端并不会应答此次的连接确认,也就不会去发送数据,因为客户端没有发送连接请求;但服务端却认为客户端向与自己建立连接,同时在等待着客户端发送数据,此时就产生了失效的连接,失效的连接会浪费服务端的资源,所以就需要三次握手来防止这种现象,只要服务端没有收到第三次握手的确认,就会知道之前的连接是一个失效的连接,就不会去浪费资源等待了
  • 四次握手的情况
    • 三次握手起始已经建立了一个可靠的连接了,后续的握手都已经是属于浪费资源了(网络答案)
    • 因为连接请求和确认连接响应仅仅在前两次握手中发生,到第四次握手已经不会出现无效连接的情况了!后续的握手都是浪费资源罢了
  • 拓展
    • 起始TCP的连接问题本质上是一个两军问题,红军和蓝军约定一个时间去进攻敌军,红军派人去告诉蓝军进攻时间,红军不知道蓝军是否收到,所以蓝军收到后,派人回复已经收到,但蓝军此时并不知道红军是否收到确认,需要红军回复,红军回复了之后,并不确认蓝军是否收到,又需要蓝军回复,这是一个无底洞的问题
    • 而TCP的对于两军问题为了保证尽可能的可靠,采用了三次握手,三次握手本质上让主动权交到了服务端那方,也就是服务端必须知道客户端准备好了,也就是进入了连接建立状态!服务端才会进入等待连接建立状态
    • 说白了起始就像是,红军决定进攻,蓝军为了怕红军卖队友,必须知道红军已经做好进攻准备了,蓝军自己才决定进攻

TCP的连接释放

  • 当数据传输结束后,通信的双方都可以去释放连接,此时的通信双方都处于established状态
  • TCP释放连接需要进行四次挥手,只有完成了四次挥手,才会停止发送数据,四次挥手的过程中依然会发生有数据的传送
  • 第一次挥手:第一次挥手其实也就是客户端发送完了所有的字节之后进行,客户端发送连接释放请求,会先确定发送的最后一个字节序号的下一个序号为U,此时FIN标志位为1,seq标志位为U,此时客户端进入FIN_WAIT状态,客户端停止了对服务端发送数据,但此时仍然会超时重发第一次挥手报文
  • 第二次挥手:服务端接收到客户端的连接释放请求,会确认连接释放请求,会确认自己前面已发送过的最后一个确认字节的下一个序号V,发送的确认报文段的ACK为1、ack为U+1、seq为V,此时服务端进入了CLOSE_WAIT状态,等待关闭,此时的TCP连接处于半关闭的状态,但服务端仍然可以对客户端发送数据,因为可能有一些仍未发送的数据滞留在了服务端;但客户端已经不能发送数据了,所以称为半关闭;并且此时客户端接收到了连接释放响应后,会进入FIN_WAIT-2状态,等待服务端发送下一个连接释放报文
  • 第三次挥手:当服务端发送完所有滞留的数据后,就会发送下一个连接释放报文,此时会去更新已经发生的、最后一个的字节报文的下一个序号W,此时FIN为1、ACK为1、seq为W,ack为U+1(继续确认前面的连接释放请求),此时服务端就进入了LAST_ACK状态,进入了最后确认状态,等待客户端响应然后关闭
  • 第四次挥手:客户端接收到服务端的第二个确认释放连接报文,会进行最后的响应,此时报文的ACK为1、seq为U+1(仍然是最后发送的字节的下一个序号)、ack为W,此时客户端不会立马关闭,而是会进入TIME_WAIT状态,时间等待,此时TCP连接还没正式释放掉,此时会开启时间等待计数器,然后等待2个MSL(Maximun Segment LifeTime,最长报文寿命,也就是2分钟)才会正式释放连接;当服务端收到了最后的确认时,就会进入关闭状态,然后关闭连接,进入CLOSED状态
  • 前三次握手是必须的,第一次握手,让服务端知道客户端要关闭连接;第二次握手和第三次握手,是为了让服务端那边将数据都给发送完
  • 那么第四次握手有什么必要性吗?为什么客户端还要开启时间等待计数器呢?为什么是2个MSL呢?
    • 第四次握手的必要性:如果第三次握手后,此时由服务端进行发送报文,必须要等待确认才能关闭,否则丢失了就无法重传了,客户端也不能关闭了!
    • 时间等待计数器的第一个作用是保证最后客户端最后发送的确认ACK报文可以被服务端接收到,如果服务端接收不到,则会进行超时重发,如果客户端在2个MSL时接收到了重发的第三次握手的报文,那么就会重启时间等待计数器,2个MSL刚好是2个报文的最长寿命,也就是收到重发报文的最久需要时间了
    • 时间等待计数器的第二个作用是让与当前连接有关的报文都从网络中消失,避免旧连接的报文仍然保存在网络上,从而被下一次新的连接所接收,因为在第三次握手后,可能仍然存在一些服务端发送的数据还在网络中传播
  • 最后进入了CLOSED状态后,就会撤销相应的传输控制块TCB
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值