【计算机网络】TCP的可靠传输机制、标记位以及编程结构

news/2024/11/13 6:43:04/

文章目录

  • 一、TCP的可靠传输的工作原理
    • 1、确认应答机制和捎带应答机制
    • 2、超时重传
    • 3、快速重传
    • 4、滑动窗口
    • 5、流量控制 未 PSH
    • 6、拥塞控制
    • 7、延迟应答
    • 8、TCP 以段为单位发送数据
  • 二、TCP 首部的六个标记位
    • 1、URG
    • 2、ACK
    • 3、PSH
    • 4、RST
    • 5、SYN
    • 6、FIN
  • 三、TCP网络并发编程

一、TCP的可靠传输的工作原理

TCP 旨在构建在 IP 层之上的一种稳定的数据传输服务。IP层虽然能够将数据包从一端传输到另一端,但其服务性质是尽力而为,不保证数据的可靠到达。TCP的设计目标是在这样的基础上,提供一种确保数据完整性和顺序性的传输机制。也就是说,TCP 提供可靠数据传输服务保证接收方进程从缓冲区读出的字节数与发送方发出的字节流完全一样。TCP 使用了校验、序号、确认和重传等机制来达到这一目的。

1、确认应答机制和捎带应答机制

TCP 实现可靠传输的方式之一,是通过序列号与确认应答。在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已经收到消息的通知。这个消息称为确认应答(ACKAcknowledgement)。

TCP 首部的确认号是期望收到对方的下一个报文段的数据的第一个字节的序号。

在这里插入图片描述

当 TCP 的接收方成功接收到数据段后,它会向发送方发送一个 ACK 报文,这个 ACK 报文中包含了接收方期望收到的下一个数据段的序列号。通过发送 ACK,接收方告诉发送方,直到这个序列号之前的数据都已经成功接收。

在这里插入图片描述

如果一段时间内没有收到确认应答,发送方就可以认为数据已经丢失,并进行重发。在这种情况下,即使产生了丢包,也能保证数据能够到达对端,实现可靠传输。但未收到确认应答并不意味着数据一定丢失,也可能是确认应答在传输过程中丢失。这种情况也会导致发送方因为没有收到确认应答,而认为数据没有到达目的地,从而进行重新发送。

如果收到很多重复数据,那么 TCP 协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。这时候我们可以利用前面提到的序列号,就可以很容易做到去重的效果。

上面这些确认应达的功能都可以通过序列号实现。接收端查询接收数据的 TCP 首部中的序列号和数据的长度,将自己下一步应该接受的序号作为确认序号发送 ACK 回去。这样就可以通过确认序号和序列号实现可靠传输。

  1. 数据包确认

每个 TCP 的报文段都有一个序列号,用来标识这个数据包中的第一个字节在整个数据流中的位置。当接收方收到一个数据包时,会发送一个确认应答回给发送方。确认包中包含一个确认序号,表示期望接收到的下一个字节的序号。

例如,如果发送方发送了一个序列号为1001且长度为100字节的数据包,接收方在收到该数据包后,会发送一个确认号为1101的ACK包,表示已经收到从1001到1100的所有字节,期待下一个字节的序列号为1101。

  1. 累积确认

TCP 的确认机制是累积的,这意味着接收方发送的确认号不仅确认了当前收到的数据包,还隐含确认了所有之前按序收到的数据包。比如,如果接收方已经收到序列号从1001到1500的所有数据包,那么它可以发送一个确认号为1501的ACK包。

捎带应答机制是TCP协议中用于提高网络传输效率的一种技术。它的基本思想是在数据传输过程中,将确认应答(ACK)信息“捎带”在数据包中一起发送,而不是单独发送一个仅包含ACK信息的报文。

  1. 正常数据传输:在 TCP 通信过程中,当发送方有数据要发送时,它会将数据封装在TCP段中,并设置相应的序列号。
  2. 捎带ACK:如果接收方在收到数据段后,有需要发送的ACK信息,它不会立即单独发送ACK,而是等待有数据发送回发送方时,将ACK信息捎带在数据段中。
  3. 延迟确认:在某些情况下,即使接收方暂时没有数据发送,它也可能不会立即发送ACK,而是等待一小段时间(通常为200毫秒左右),看看是否有数据需要发送回对方,从而实现捎带。

捎带应答机制需要两个序列号,一个是发送方的序号,用于标志发送方发送数据段的起始字段。另一个是确认序号,用于标志接收方期望发送方收到的下一个字节。

2、超时重传

有两种事件会导致 TCP 对报文段进行重传:超时和冗余ACK

超时重传:在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据。(超时重传机制,该确定可靠性的机制没有在报头中体现)

由于 TCP 的下层是一个互联网环境,IP 数据报所选择的路由变化很大,因而传输层的往返时延的方差很大,为了计算超时计时器的重传时间。

TCP 会在以下两种情况发生超时重传:数据包丢失和确认应答丢失

丢包会导致接收方无法返回 ACK,发送方在等待超时后会触发重传;接收方成功接收数据包并返回 ACK,但 ACK 在传输过程中丢失。发送方未能在规定时间内收到 ACK,因此认为数据未成功传输,从而引发超时重传。

  1. 发送数据:当TCP发送数据时,它会为每个发送的数据包分配一个序列号。
  2. 确认接收:接收方收到数据包后,会发送一个确认包(ACK)给发送方,确认已经收到特定序列号的数据包。
  3. 计时器启动:发送方在发送数据包时,会启动一个计时器。如果在这个计时器到期之前没有收到对应的ACK,发送方会认为这个数据包(以及随后的所有未确认的数据包)已经丢失。
  4. 超时重传:当计时器超时,发送方会重新发送未被确认的数据包。

如果在计时器到期之前收到了 ACK,定时器会被取消,认为该数据包已成功发送。

在ACK丢失的情况下,接收方可能接收到重复的报文。但 TCP 协议有内置的机制来处理这种情况,以确保接收方不会因为重复的报文而出现数据不一致的问题。

TCP 使用序号来对每个数据包进行编号。每个 TCP 数据段都有唯一的序列号,接收方根据序列号来判断数据包的顺序和是否是重复的。

接收方会根据每个数据包的序号来排列数据,即使接收到重复的数据包,也会检测到序列号已存在,从而丢弃重复的包,而不将其交给上层应用程序处理。

注意:数据不会被无限地、反复地重发。达到一定重发次数后,如果多次重传仍然没有得到应答,TCP 会认为网络环境异常(例如:目标主机不可达、网络故障等),这时 TCP 会主动关闭连接。关闭连接的方式通常是发送一个 RST 报文或者在本地终止该连接,释放相关资源。

重传时间如何确定? 取决于网络的状态(网络状态是动态的,因此重传时间也是动态的)

由于TCP下层是一个一个互联网环境,IP 数据报所选择的路由变化很大,因而传输层的往返时延的方差也很大。为了计算超时重传的时间,TCP 采用一种自适应的算法,它记录一个报文段的发出的时间,以及收到确认应答的时间。这两个时间之差称为报文段的往返时间(RTT,Round-Trip-Time)。TCP保留了 RTT 的一个加权平均往返时间 RTTs,它会随新测量 RTT 的值变化而变化。

在 Linux 以及Windows系统中,超时都以0.5秒为单位进行控制,因此重发超时都是0.5秒的整数倍(偏差的最小值也是0.5秒。因此最小的重发时间至少是1秒。) 。不过,由于最初的数据包还不知道往返时间,所以其重发超时一般设置为6秒左右。数据被重发之后若还是收不到确认应答,则进行再次发送。此时,等待确认应答的时间将会以2倍、4倍的指数函数延长。

3、快速重传

上文提到,有两种事件会导致 TCP 对报文段进行重传:超时和冗余ACK。冗余ACK就是快速重传机制。

超时重传存在的一个问题就是超时周期太长。发送方通常可以在超时重传之前,通过发现冗余的ACK来检验丢包情况。冗余ACK就是再次确认某个报文段的ACK,而发送方先前已收到过该报文段的确认。

当发送方收到三个重复的确认应答(ACK),它会触发快速重传机制,重新发送丢失的数据报文。这里的关键是确认应答的确认序号,它用于确定哪个数据报文丢失并需要重传。这个机制用于快速恢复丢失的数据报文,减少传输延迟。

  1. 接收方丢包处理:当接收方收到的数据报文丢失(例如,报文乱序或未到达),它会继续发送确认应答(ACK),以告知发送方期望接收到的下一个字节的序列号。如果接收方已经正确接收到序列号为1001、1001的数据报文,但序列号为1003的报文丢失,它会连续发送确认序号为1003的ACK报文。
  2. 发送方检测重复ACK:发送方在接收到三个相同的确认应答(重复ACK)时,判断接收方未收到的报文是丢失的。例如,发送方连续收到三个ACK号为1003的确认报文,表明序列号为1003的报文可能丢失,没有被接收方确认。
  3. 执行快速重传:根据最后一个重复的确认应答的确认序号(ACK号),发送方重新发送丢失的数据报文。在这个例子中,发送方会重新发送序列号为1003的报文。
  4. 更新滑动窗口:重新发送数据后,发送方会更新滑动窗口的状态,调整窗口开始指针和窗口结束指针,以反映重传数据和新的确认状态。窗口开始指针不会立即移动,直到收到对重新发送数据的确认。
  5. 接收方处理重传数据:一旦接收到重传的数据报文,接收方会处理数据并更新确认应答。接收方可能会继续发送新的确认应答,通知发送方已经接收到的数据和期望接收的下一个数据序列号。

既然有了快速重传机制,为什么还要有超时重传机制?

快速重传机制和超时重传机制都是TCP协议中用于确保数据可靠传输的机制,但它们各自应对不同的丢包情况,并且有各自的作用。

快速重传

  • 触发条件:当发送方收到三个重复的ACK时,它会触发快速重传机制。
  • 目的:快速重传是为了减少因单个报文丢失而导致的等待时间。当发送方检测到重复的ACK时,它不需要等待整个重传计时器超时,就可以立即重传丢失的报文。

这种方法可以更快地恢复丢包,减少网络延迟。

超时重传机制

  • 触发条件:当一个报文发送后,如果在设定的时间内没有收到确认(ACK),则认为该报文丢失,会触发超时重传机制。
  • 目的:超时重传是TCP协议中的基本可靠性保障机制,用于处理所有类型的丢包情况,包括那些没有通过重复ACK被立即检测到的情况。
  • 以下是需要超时重传机制的原因
    1. 非连续丢包:如果网络中发生了多个连续的报文丢失,可能不会有足够的重复ACK来触发快速重传机制。在这种情况下,超时重传是必要的。
    2. 延迟ACK:在某些情况下,接收方可能会延迟发送ACK,这可能导致发送方无法及时收到重复的ACK,因此无法触发快速重传。
    3. 网络拥塞:在网络拥塞严重的情况下,即使是单个报文的丢失也可能不会导致重复ACK,因为后续报文的传输可能同样受到阻碍。
    4. 错误的序列号:如果由于某些原因接收方发送了错误的ACK序列号,这可能导致发送方无法正确触发快速重传。
    5. 处理所有情况:超时重传机制是TCP协议中一个全面的故障恢复机制,可以处理所有类型的丢包情况,而不仅仅是通过重复ACK检测到的丢包。

快速重传和超时重传是TCP协议中互补的机制。快速重传提供了一种快速响应丢包的方法,而超时重传则为所有可能的丢包情况提供了一个后备的可靠性保障。这两种机制共同工作,确保了TCP传输的可靠性和效率。

4、滑动窗口

UDP 报文的长度由发送应用进程决定,而TCP 报文的长度是根据接收方给出的窗口值和当前网络的拥塞状况来决定的。

如何理解滑动窗口

在TCP早期版本中,为了确保数据传输的可靠性,使用了简单的停止-等待协议,即发送方发送一个数据段后,必须等待接收方的确认(ACK)才能发送下一个数据段。这种方法效率低下,因为它没有充分利用网络带宽。

为了提高效率,TCP引入了滑动窗口的概念,它允许发送方在等待确认的同时发送多个数据段。

在这里插入图片描述

滑动窗口的大小

滑动窗口的大小指的是发送端在未收到接收端确认之前,能够连续发送的数据量。这个大小由接收端的缓冲区大小决定(接收端接收缓冲区剩余的大小,因此滑动窗口大小是会改变的),并在每个数据包的确认报文中更新。

窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。

滑动窗口本质

这个机制实现了使用大量的缓冲区(缓冲区在此处表示临时保存收发数据的场所。通常是在计算机内存中开辟的一部分空间。) ,通过对多个段同时进行确认应答的功能。

滑动窗口本质上是发送缓冲区的一部分。它代表了发送端在未确认的数据区域内,可以连续发送的数据量。这部分数据在发送前无需等待确认。

窗口大小由哪一方决定

在 TCP 报头中有一个窗口大小的16位字段,表示接收方可以接收的字节数。

这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。

缓冲区大小的影响

  • 接收端的接收缓冲区限制了可以接收的数据量。滑动窗口的大小反映了接收端当前缓冲区的可用空间。
  • 当接收端的缓冲区满了,窗口大小会变小。处理数据后,接收端会更新窗口大小,允许发送端发送更多数据。

窗口的动态调整

  • 接收端在每个ACK报文中会更新窗口大小字段,告知发送端当前的接收能力。
  • 发送端根据接收端提供的窗口大小来调整数据发送量,确保不会超出接收端的处理能力。

举例来说,如果接收端的缓冲区剩余8KB,滑动窗口的大小就是8KB。发送端可以在不等待确认的情况下连续发送8KB的数据。一旦接收端处理了一部分数据并释放出空间,它会在ACK报文中更新窗口大小字段。例如,如果缓冲区空间增加到12KB,发送端可以继续发送额外的数据。

滑动窗口的作用

确保发送端的数据流量控制

  • 滑动窗口的大小由接收端的缓冲区决定,它表示接收端在不进行确认的情况下可以接收的数据量。
  • 发送端可以在未收到确认的情况下发送的数据量不超过这个窗口大小。这样可以避免发送端发送过多数据而导致接收端的缓冲区溢出。

避免接收端的数据丢失

  • 滑动窗口机制确保了接收端的缓冲区不会被超量的数据填满,从而防止了数据溢出和丢失。
  • 接收端会在每个确认报文中更新窗口大小,以反映当前接收缓冲区的可用空间。发送端会根据这个信息调整发送的数据量,从而避免数据越界。

滑动窗口机制通过调整窗口大小来协调发送端和接收端的数据传输,确保不会超出接收端的处理能力。

TCP发送方的发送缓冲区中的数据

  • 已发送已确认的数据:已经被接收方确认的数据,不需要再处理。发送方缓冲区中滑动窗口滑过的区域这些数据已经发送给接收方,并且接收方已经确认(ACK)。这些数据不再需要处理,因为接收方已成功接收到并确认了这些数据;
  • 滑动窗口中间的数据:在当前窗口范围内,可以继续发送的数据。这些数据是在当前滑动窗口的范围内,发送方可以在未收到确认之前继续发送这些数据。滑动窗口的大小由接收方的窗口大小字段决定,并在ACK报文中动态更新;
  • 待发送的数据:尚未到达当前窗口范围的数据,需等待窗口滑动后才能发送。这些数据还未到达滑动窗口的范围,当前不能发送,等待发送,直到滑动窗口滑过这些数据。发送方需要等待接收方的确认以更新窗口大小,从而允许继续发送这些待发送的数据。

我们考虑确认应答未能返回的情况,在这种情况下,数据已经到达对端,是不需要进程重发的。然而所在没有使用窗口控制的时候,没有收到确认应答的报文都得重发。但是有了窗口,某些确认应答丢失了也无需重发。

在这里插入图片描述

那么下面考虑,某一段报文丢失的情况。

在这里插入图片描述

当某一报文段丢失后,发送端会一直收到序号为1001的确认应答,这个确认应答好像在提醒发送端“我想接收的是从1001开始的数据”。因此,在窗口比较大,又出现报文段丢失的情况下,同一个序号的确认应答将会被重复不断地返回。而发送端主机如果连续3次收到同一个确认应答(之所以连续收到3次而不是两次的理由是因为,即使数据段的序号被替换两次也不会触发重发机制。) ,就会将其所对应的数据进行重发。

我们进行一下总结,滑动窗口如何支持超时重传?

对发送的报文,并且没有收到的应答的报文进行保存(如何保存,保存在滑动窗口内部,若没有收到应答,滑动窗口不会向右滑动,会把滑动窗口卡在那,如果发现丢失,进行重传。只有收到了ack才会窗口滑动),方便我们进行重传。

根据上文的描述,我们可以发现,滑动窗口只能向右滑动。滑动窗口的左边界(开始指针)只有在收到对应数据的ACK确认时才会向右移动。

滑动窗口的大小可以根据接收端的处理能力和网络状况动态调整。当接收端处理数据后,有更多的缓冲区空间时,窗口可以增大;当接收端的缓冲区空间不足时,窗口可以减小。滑动窗口的大小也可以为0。这通常发生在接收端的缓冲区已满,无法接收更多的数据时。此时,发送端会停止发送数据,直到接收端发送非零窗口大小的ACK。

我们先来看看发送方的窗口,下图就是发送方缓存的数据,根据处理的情况分成四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口:

在这里插入图片描述

滑动窗口大小变大:窗口大小可以增大。当接收端处理了数据并释放出更多的缓冲区空间时,它会在 ACK 报文中更新窗口大小,从而允许发送端发送更多的数据。

滑动窗口大小变小:窗口大小可以变小。当接收端的缓冲区使用越来越多,或处理数据的速度跟不上发送的速度时,接收端会减小窗口大小。这可以限制发送端的数据量,防止接收方缓冲区溢出。

滑动窗口大小为0:窗口大小可以为0。当接收端的缓冲区满了时,它会在 ACK 报文中将窗口大小设置为0,告知发送端停止发送数据,直到接收端有更多的缓冲区空间可用。

也就是,通过两指针维护一段数组空间。

窗口开始指针:指向当前滑动窗口的开始位置,即最早发送的数据的序列号。

窗口结束指针:指向滑动窗口的结束位置,即当前滑动窗口的最大位置。

发送过程中出现丢包,滑动窗口怎么滑?

  • 最左侧报文丢包
    • 如果滑动窗口的最左侧报文丢失,接收端会重复确认已接收到的最后一个报文,发送端在收到三个重复的ACK后,会触发快速重传机制,重新发送丢失的报文。一旦丢失的报文被成功接收并确认,窗口的左边界才会向右滑动。
  • 中间报文丢包
    • 如果滑动窗口中间的报文丢失,接收端同样会重复确认它期望的下一个报文,即丢失报文之前的报文。发送端在收到重复的ACK后,会触发快速重传丢失的报文。一旦丢失的报文被重传并确认,窗口的左边界会跳过丢失的报文,直接移动到重传报文的下一个序列号。
  • 最右侧报文丢包
    • 如果滑动窗口最右侧的报文丢失,接收端无法确认这个报文,因此不会发送新的ACK来确认它。发送端可能会等待这个报文的ACK超时,然后触发超时重传。一旦丢失的报文被重传并确认,窗口的右边界会向右滑动。

在滑动窗口内,任何位置的丢包都会导致窗口左边界无法向右滑动,因为TCP要求按序确认。所以,无论哪个位置的报文丢失,最终都需要通过重传丢失的报文来确保窗口能够按序滑动。快速重传和超时重传机制确保了TCP的可靠性,通过重传丢失的报文来恢复正确的数据流顺序。

5、流量控制 未 PSH

TCP 提供流量控制来消除发送方因发送速率太快,使接收方缓冲区溢出的可能。 因此,可以说,流量控制是一个速度匹配服务(匹配发送方的发送速率与接收方的读取速率)。

它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小。

流量控制就是 TCP 使用滑动窗口机制实现的

在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方的窗口大小,这称为接收窗口。即通过调整TCP 报文段首部的”窗口“字段的值,来限制发送方的发送速度。同时发送方也会根据当前网络的拥塞程度来确定窗口值,这个窗口是拥塞窗口(后文介绍)。

不过,接收端的这个缓冲区一旦面临数据溢出时,窗口大小的值也会随之被设置为一个更小的值通知给发送端,从而控制数据发送量。也就是说,发送端主机会根据接收端主机的指示,对发送数据的量进行控制。这也就形成了一个完整的TCP流量控制。

在这里插入图片描述

如图所示,当接收端收到从3001号开始的数据段后其缓冲区即满,不得不暂时停止接收数据。之后,在收到发送窗口更新通知后通信才得以继续进行。如果这个窗口的更新通知在传送途中丢失,可能会导致无法继续通信。为避免此类问题的发生,发送端主机会时不时的发送一个叫做窗口探测的数据段,此数据段仅含一个字节以获取最新的窗口大小信息。

由于发送太快和太慢都有可能发生,因此我们需要流量控制。

TCP自主进行流量控制。具体而言,主机B通过发送确认应答(ACK)给主机A,以此告知其当前的接收能力,填写TCP头部的窗口大小。该ACK不仅确认了数据的成功接收,同时也携带着主机B的窗口大小信息,从而允许发送方根据接收方的处理能力来调整数据传输速率。

当主机B(接收方)通知主机A(发送方)其接收缓冲区已满时,这是通过在TCP头中设置窗口大小字段为0来实现的。这表示主机B暂时无法接收更多的数据。主机A在收到这样的通知后,必须停止发送数据。那么主机A如何知道什么时候才能再发数据呢?

主机A知道何时可以再次发送数据的两种机制如下:

  1. 窗口探测(WindowProbe)
    • 当主机A收到窗口大小为0的ACK时,它会启动窗口探测机制。
    • 主机A会定期发送小的数据段(通常只包含一个字节的数据),称为窗口探测包(WindowProbe Segment),以询问主机B何时能够接收数据。
    • 主机B在准备好接收数据时,会在回复的ACK中包含新的窗口大小,告诉主机A可以开始发送数据了。
  2. 窗口更新(WindowUpdate)
    • 一旦主机B的接收缓冲区有空间,它会发送一个窗口更新ACK给主机A,这个ACK会包含新的窗口大小,表明主机A现在可以发送多少字节的数据。
    • 主机A在收到窗口更新ACK后,就可以根据更新后的窗口大小开始发送数据。

当主机A向主机B传输数据的速率超过主机B的处理能力,导致主机B的接收缓冲区满载,此时新到达的报文将无法被存储,从而不得不被丢弃,这种情况称为丢包问题。尽管TCP协议能够通过重传机制处理丢包而不会中断通信,但这种频繁的丢包会不必要地消耗网络资源和带宽资源,因此是不合理的。为解决这一可靠性问题,主机B在向主机A发送确认应答(ACK)时,可以包含其当前的接收能力信息,以此来告知主机A调整发送速率。

另一方面,如果数据传输速率过低,则会影响传输效率,这属于流量控制问题。在这种情况下,主机B同样可以通过调整其通告的接收能力,鼓励主机A提高数据发送速率,从而优化整体网络的传输效率。通过这种方式,TCP协议能够动态地平衡发送方的数据传输速率与接收方的处理能力,确保通信的可靠性和高效性。

6、拥塞控制

拥塞是指网络中的数据包数量超过了网络设备的处理能力,导致网络性能下降,甚至无法正常工作的情况。当网络拥塞发生时,数据包会在网络中积累,导致延迟增加、数据包丢失、传输速率降低,甚至连接失败。

拥塞控制是指防止过多的数据注入网络,保证网络中的路由器或链路不致过载。出现拥塞时,端点并不了解拥塞发生的细节。对通信连接的端点来说,拥塞往往表现为通信时延的增加。由于网络问题,引起的可靠性问题,就是拥塞控制的目的。

拥塞控制与流量控制的区别

拥塞控制是让网络能够承受现有的网络负荷,是一个全局性的过程,涉及所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点的通信量的控制,是个端到端的问题,它所要做的是抑制发送端发送数据的速率,以便来得及接收。当然,拥塞控制和流量控制有相似的地方,即它们都是通过控制发送方发送数据的速率来达到控制效果。

发送方在确定发送报文段的速率时,既要根据接收方的接受能力,又要从全局考虑不要使网络发生拥塞。因此,TCP 需要维护以下两个窗口:

  1. 接收窗口:接收方根据目前接收缓存的大小所许诺的最新窗口值,反映接收方的容量。由接收方根据其放在 TCP 报文的首部的窗口字段通知发送方。
  2. 拥塞窗口:发送方根据自己估算的网络拥塞出现而设置的窗口值,反应网络的当前容量。只要网络未出现拥塞,拥塞窗口就再增大一些,以便能发送更多的数据。但只要网络出现拥塞,拥塞窗口的大小就会减小。拥塞窗口是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。

发送窗口的上限值应取接收窗口和拥塞窗口中较小的一个。

一般来说,计算机都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络堵塞。在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致网络的瘫痪。

TCP 为了防止该问题,在通信一开始就会通过一个叫做慢启动的算法得出的数值,对发送数据量进行控制。

  1. 初始化:当TCP连接建立时,发送方的拥塞窗口 cwnd 被初始化为一个很小的值,通常是1个最大段大小(MSS)。
  2. 线性增长:每经过一个往返时间(RTT),拥塞窗口的大小会加倍,即cwnd = cwnd × 2。这样,发送速率会逐渐增加,直到达到一个阈(yu)值,这个阈值称为慢启动阈值。
  3. 阈值:当拥塞窗口的大小达到慢启动阈值时,TCP会进入拥塞避免阶段。在这个阶段,拥塞窗口的大小不再以指数方式增长,而是以线性方式增加,即cwnd = cwnd + 1。
  4. 拥塞检测:如果网络出现拥塞,例如,由于发送方发送的数据量过大导致网络堵塞,网络设备可能会丢弃部分数据包。当发送方检测到有数据包丢失时,它会将拥塞窗口的大小减少到慢启动阈值,并重新开始慢启动过程。
  5. 重传:如果发送方在一定时间内没有收到确认(ACK)应答,它可能会认为数据包丢失,并触发超时重传机制。在这种情况下,发送方会将拥塞窗口的大小减少到慢启动阈值,并重新开始慢启动过程。

“慢启动” 只是指初使时慢, 但是增长速度非常快。指数级增长 2n

当TCP通信开始以后,网络吞吐量会逐渐上升,但是随着网络拥堵的发生吞吐量也会急速下降。于是会再次进入吞吐量慢慢上升的过程。因此所谓TCP的吞吐量的特点就好像是在逐步占领网络带宽的感觉。

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

本文仅介绍了慢启动。

7、延迟应答

TCP延迟应答是一种优化技术,用于提高TCP连接的效率。在传统的TCP实现中,当接收方接收到一个数据段时,它会立即发送一个ACK确认。然而,这可能导致网络中的确认报文数量过多,尤其是在接收方不需要立即发送数据时。

通过延迟一部分应答,让发送方得到一个更大的窗口,从而整体上提高TCP传输效率。收到一个报文后,不立即发送ACK,而是等一会再发送ACK。在等的时候,上层把数据取走,ACK时就可以告诉发送方一个更大的接收窗口。

TCP延迟应答的工作原理:

  1. ACK确认:当接收方接收到一个数据段时,它会等待一段时间,以查看是否有更多的数据需要发送。如果接收方准备发送数据,它会将ACK确认捎带在即将发送的数据段中。
  2. 减少网络流量:通过延迟发送ACK确认,减少了网络中不必要的确认报文数量,从而降低了网络流量。
  3. 提高效率:延迟应答减少了发送方发送数据后等待ACK确认的时间,提高了TCP连接的效率。

延迟应答的实现

TCP实现通常包含一个延迟应答定时器,当接收方接收到数据段时,它会启动这个定时器。如果定时器到期,接收方会发送一个ACK确认。如果在这个时间段内,接收方准备好发送数据,它会将ACK捎带在即将发送的数据段中。

8、TCP 以段为单位发送数据

在建立 TCP 连接的同时,也可以确定发送数据包的单位,我们也可以称其为“最大消息长度”(MSS,Maximum Segment Size)。最理想的消息长度正好是在 IP 层不会被分片处理的最大数据长度。

TCP在传送大量数据时,是以 MSS 的大小将数据进行分割发送。进行重发时也是以 MSS 为单位。

那么首次发送的时候应该发送多少数据?

首次发数据,不是首次发报文,因此在建立连接时,会把相关内容告诉对方。前两次握手不能带数据(三次握手没完成双方没有正式建立连接),但第三次ack可以携带数据。

如果不握手就发,会收到服务端发送的携带 RST 的报文,要我们重新建立连接。因此,无论如何一定要进行三次握手。

MSS是在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在TCP首部中写入MSS选项,告诉对方自己的接口能够适应的MSS的大小。最后在双方的MSS的大小中选一个较小值投入使用。

在这里插入图片描述

在TCP中,MSS(最大段大小)和MTU(最大传输单元)是网络通信中的重要概念。

MTU(Maximum Transmission Unit)是指网络层协议能够传输的最大数据包大小,通常以字节为单位。它决定了在数据链路层一个网络帧可以承载的数据量。不同类型的网络技术有不同的MTU值,例如,以太网的MTU通常是1500字节。

在这里插入图片描述

MSS(Maximum Segment Size)是TCP协议定义的一个选项,用于在TCP连接建立时,告知对端在TCP分段中能够发送的最大数据量(不包括TCP头部)。MSS的目的是为了避免IP层对数据进行分片。

计算关系

  • MSS通常是基于MTU计算得出的。为了确保TCP数据包在不需要分片的情况下通过网络,MSS的值通常是MTU值减去IP头和TCP头的总和。对于IPv4,IP头通常是20字节,而TCP头通常是20字节,所以计算公式通常是:
    MSS = MTU - IP头大小 - TCP头大小
    
    对于以太网(MTU = 1500字节):
    MSS = 1500 - 20 (IP头) - 20 (TCP头) = 1460 字节
    

总结来说,MSS是TCP层考虑头部大小后,基于底层网络的MTU来确定的,以确保TCP数据包能够在不进行IP分片的情况下高效传输。正确配置MSS对于优化网络通信性能至关重要。

TCP 的粘包问题主要是因为它是 面向字节流 的协议,而不是像 UDP 那样的 面向数据报 的协议。TCP 的设计目的是为了确保数据能够可靠、有序地传输,但它没有定义消息的边界。因此,当多个数据包在发送时,它们可能会被合并或拆分在一起传输,这就引发了 粘包 问题。这种情况下,接收方需要通过应用层协议来解析和拆分数据,防止粘包问题。

UDP 不存在粘包问题:每个UDP数据包(数据报)在发送时都是一个独立的、完整的消息。每个UDP数据包在传输过程中保持原样,不会被合并或分割。因此,接收方接收到的数据包与发送方发送的数据包是一一对应的。


二、TCP 首部的六个标记位

1、URG

URG标志位: 一个用于处理紧急数据的控制位。URG指针用于标识数据流中需要紧急处理的数据位置。紧急数据是在数据流中传递的一种特殊数据(这种数据在数据流中并没有被单独分离出来,而是通过在TCP报头中的URG指针来标记。),通常用于在传输过程中通知接收端某些数据需要立即处理,而不必等待完整的数据流接收完毕。TCP中紧急数据只有一个字节。

当URG标志位被设置为1时,TCP头部中的16位紧急指针会指示紧急数据的结束位置。这使得接收方知道哪部分数据是紧急的,并可以优先处理这些数据。

在TCP中紧急指针一般只表示一个字节。告诉接收方从当前数据段的哪个位置开始是紧急数据。紧急指针用于指出在数据段中,紧急数据的结束位置。它表示从当前序列号开始,多少字节的数据是紧急的,需要立即处理。即用于指明紧急数据的结束位置,是一个相对于当前TCP序列号的偏移量。

send函数可以用于读取带外数据(Out-Of-Band Data) MSG_OOB。带外数据是一种特殊的数据类型,可以在TCP数据流中优先处理,但通常用于表示紧急信息或某种控制信息。

2、ACK

ACK表示确认应答标记位。在TCP确认应答机制中,客户端和服务端任意一方发送数据后,另一方都需要给予应答以表明自己收到数据。在应答的报文中该标记位需要置1,同时应答的报文也可以携带数据。

3、PSH

PSH(Push,推送功能位) 是一个用于控制数据传输的标志位。当 PSH 位被设置为 1 时,表示该数据应立即被推送到接收方的应用层,而不需要再等待更多的数据或缓存。PSH 位的主要作用是在需要快速传输和响应的数据传输场景下,使接收方能够立即处理数据,而无需等待缓冲区满或其他条件。

通常,TCP 在传输数据时会对数据进行缓冲处理,即发送方会积累一定量的数据,等到有足够多的数据时再将其打包发送,接收方也会先将接收到的数据放入缓冲区,直到缓冲区的数据达到一定量后再交给应用程序。但在某些情况下,发送方希望数据能够立即到达接收方的应用层,而不是延迟处理,这时就会使用 PSH 位。

当接收方检测到 PSH 位被设置时,通常会在收到当前报文段后立即将数据从接收缓存区推送到应用层,而不是等待更多数据段来填充缓冲区。

但即使 PSH 位被设置,TCP 的数据仍然是按序传递的。PSH 位仅仅是通知接收方不必等待更多的数据,而不是打破数据流的顺序。

在发送方:TCP 协议栈可能会在数据达到一定数量后才进行发送。设置 PSH 位后,发送方立即发送数据,而不会等到缓冲区满。

在接收方:接收方通常会等待 TCP 缓冲区中的数据达到一定量时才交给应用层处理。PSH 位告诉接收方,当前数据不需要再等待,可以立即传递给应用层。

PSH 只能影响操作系统层。 PSH 位需要接收方的应用层能够配合处理。如果接收方的应用层没有按照 TCP 的 PSH 位进行快速响应,PSH 位的作用可能不会如预期发挥效果。

以下是一些具体的使用场景:

  • 交互式会话:在Telnet或SSH等交互式会话中,用户输入需要被立即处理和响应。设置PSH标记可以确保用户输入不会被延迟处理。
  • 文件传输:在文件传输过程中,如果发送方完成了一个文件块的发送,并希望接收方立即开始处理这个块,它可以使用PSH标记。
  • 实时通信:在实时音频或视频通信中,数据需要尽快被处理以保证流的连续性。

即使发送方设置了PSH标记,接收方的TCP栈仍然可能根据其内部缓存策略来处理数据。

4、RST

RST(Reset,复位) 是 TCP 协议首部中的一个控制标志位,用于强制终止一个不正常的连接。它通常在以下情况出现:当通信双方中的一方遇到意外情况或检测到连接错误,决定立即终止连接并不再进行数据传输时,RST 位将被设置为 1,通知另一方终止当前连接。

RST 标记位的作用与功能

  1. 强制重置连接:当一方收到 RST 报文时,表示当前的连接状态已经无效,连接立即终止。TCP 将不再进行重传或继续等待数据,而是立即丢弃连接相关的资源。
  2. 用于错误处理:如果一方接收到一个与当前状态不匹配的数据包(例如,在一个不存在的连接上收到数据),它会发送一个 RST 报文来通知对方该连接无效。
  3. 异常情况下的快速响应:RST 报文允许在检测到异常或无法继续通信时,双方能够快速结束连接,而无需经历正常的 FIN 四次挥手过程。

如果一方收到一个指向不存在或无效连接的 SYN(同步)请求包,它可能会返回一个 RST 包,告知发起方该连接是非法的。例如,主机 A 向主机 B 发送数据,但主机 B 上没有相应的打开连接,主机 B 就会发送一个带有 RST 位的 TCP 报文段。

当 TCP 连接的任何一方发送了带有 RST 标志的 TCP 报文时,接收方会立即终止该连接,并丢弃所有与该连接相关的资源,包括缓冲区中的未处理数据、未确认的报文等。RST 的作用是立即关闭连接而不需要进行任何额外的握手或重试。

发送 RST 报文的触发条件:

  1. 无效的 SYN 请求:当客户端发送 SYN 请求而目标主机没有相应的监听端口时,目标主机会返回一个 RST 报文,表示该连接无效。
  2. 非法的数据包:如果 TCP 连接的一方收到的 TCP 报文段与当前连接状态不匹配(例如收到的数据包序列号超出预期范围),可能会触发 RST 报文,告知对方发生了连接错误。
  3. 异常中断:在某些特殊情况下,应用程序可能会遇到错误,或者用户强制关闭了连接(例如强制关闭应用程序),这时系统会发送 RST 报文强制终止连接。

5、SYN

SYN(Synchronize)标志位是 TCP 协议中用于建立连接的标志位之一,它在三次握手过程中扮演着至关重要的角色。SYN 位的作用是在连接建立阶段,用来同步发送方和接收方的序列号,确保双方在传输数据前就能够相互确认并保持一致。

SYN 位 用于初始化一个 TCP 连接。当主机 A 想要与主机 B 建立连接时,主机 A 会发送一个设置了 SYN 位的 TCP 报文段,该报文段包含了一个初始的序列号。SYN 位为 1 时,表示该报文段是一个连接请求,它用于同步序列号,以确保后续数据传输的可靠性和顺序性。

6、FIN

TCP 首部中的 FIN(Finish)标记位 是用来表示连接的一方已经完成数据发送并希望关闭该连接。FIN 位的作用是发起 TCP 连接的 四次挥手 过程,告知另一方自己不再有数据要发送,从而进入连接终止阶段。

RST 和 FIN 虽然都用于关闭连接,但是仍存在区别:

  • RST(Reset) :RST 位用于强制立即终止连接,适用于异常或错误的情况。它不会进行任何握手流程,也不保证未传输的数据被接收方处理。RST 通常用于连接的非正常终止,表明连接出错或无效。
  • FIN(Finish) :FIN 位用于优雅地终止连接,表示一方已经完成了数据传输,并且希望正常关闭连接。FIN 关闭连接时需要进行四次握手,以确保双方都已经接收到所有数据。

三、TCP网络并发编程

TCP 客户端和服务端编程架构:

在这里插入图片描述

当服务一个请求需要花费较长时间时,我们不希望整个服务器长时间被单个客户占用,而是希望同时服务多个客户。因此,编写并发服务器程序最简单的方法就是fork一个子进程来服务每个客户。

在这里插入图片描述

当一个连接建立时,accept返回,紧接着服务器调用fork,然后由子进程服务客户,父进程则等待另一个连接。既然新的客户由子进程服务 ,那么父进程就可以关闭已连接的套接字。
在这里插入图片描述

注意,此时listenfdconnfd这两个文件描述符都在父子进程之间共享,父进程可以close已连接的套接字,子进程关闭监听套接字。

在这里插入图片描述
这时两个套接字就是,子进程处理与客户的连接,父进程在监听套接字上循环调用accept来处理下一个客户的连接。

此外,服务器进程在创建子进程之后,子进程也可以调用fork来创建一个新的子进程,从而将处理连接的任务交给子进程的子进程。这样,服务器进程的父进程就无需调用wait来等待子进程完成,因为子进程的子进程会自动成为孤儿进程,并由init进程接管和最终回收。这种方法简化了服务器进程的管理,减少了其等待时间。


http://www.ppmy.cn/news/1527551.html

相关文章

Spring Boot-RESTful API相关问题

Spring Boot RESTful API 相关问题探讨 Spring Boot 是基于 Spring 框架的简化开发工具,提供了快速构建 RESTful API 的能力。在实际开发中,Spring Boot 的 REST API 可以快速开发出符合 REST 架构风格的接口。然而,在构建 RESTful API 时&a…

数据结构——二叉搜索树、Map和Set

对于不同的数据结构,他们的使用场景是不一样的,map和set这两种数据结构主要用在搜索相关的场景中。学习这些之前我们先来了解一下二叉搜索树, 一、搜索树 1.1概念 二叉搜索树 又称 二叉排序树 ,它或者是一棵空树,或者…

Redis 底层数据结构,一文详解

Redis 底层用 C 语言实现,不同版本的数据类型使用的数据结构也不同,下面详细看 SDS 字符串在 Redis 中很常用,键值对的所有键都是字符串,值有时候也是字符串 Redis 是用 C 语言实现的,但是字符串没有直接用 C 语言的…

功能测试干了三年,快要废了。。。

8年前刚进入到IT行业,到现在学习软件测试的人越来越多,所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷,越来越多的转行和大学生进入测试行业,导致软件测试已经饱和了,想要获得更好的待…

基于鸿蒙API10的RTSP播放器(八:音量和亮度调节功能的整合)

一、前言: 笔者在前面第六、七节文章当中,分别指出了音量和屏幕亮度的前置知识,在本节当中,我们将一并实现这两个功能,从而接续第五节内容。本文的逻辑分三大部分,先说用到的变量,再说界面&…

【TypeScript】 ts控制语句

文章目录 ts控制语句1. 条件语句1.1 if 语句1.2 if...else 语句1.3 if...else if...else 语句1.4 switch...case 语句 2. 循环2.1 for 循环2.2 for...in 循环2.3 for...of、forEach、every 和 some 循环2.4 while 循环2.5 do...while 循环2.6 break 语句2.7 continue 语句 3. 函…

Golang | Leetcode Golang题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; func reconstructQueue(people [][]int) (ans [][]int) {sort.Slice(people, func(i, j int) bool {a, b : people[i], people[j]return a[0] > b[0] || a[0] b[0] && a[1] < b[1]})for _, person : range people {idx : pe…

C编程控制PC蜂鸣器方法2

在《C编程控制PC蜂鸣器》一文中,我们了解并使用了通过IO端口控制的方式操作硬件,而有些时候这对于一些朋友来说太模糊了,很容易让人迷糊,这次采用最基本的write系统调用来写入input_event数据实现相同功能。这里涉及到的input_event可参考《C编程实现键盘LED闪烁方法2》一文…