文章目录
- [3. Transport Layer(传输层)](#3. Transport Layer(传输层))
-
- [3.1 Multiplexing and demultiplexing(多路复用和多路分解)](#3.1 Multiplexing and demultiplexing(多路复用和多路分解))
- [3.2 Connectionless transport:UDP](#3.2 Connectionless transport:UDP)
- [3.3 Principles of reliable data transfer](#3.3 Principles of reliable data transfer)
- [3.4 Pipelined communication](#3.4 Pipelined communication)
- [3.5 TCP: connection-oriented transport](#3.5 TCP: connection-oriented transport)
- [3.6 Principles of congestion control](#3.6 Principles of congestion control)
3. Transport Layer(传输层)
传输层的主要作用是在不同主机上运行的应用程序进程之间提供逻辑通信。
传输层协议在端系统中的运行过程:在发送端,传输层协议将应用层的消息(也称为报文)分割成较小的数据段(也称为分组或报文段),然后将这些数据段传递到网络层。这个过程叫breaks app msg into segments, passes to network layer。
在接收端,传输层协议将接收到的数据段重新组装成完整的消息,然后将这些消息传递到应用层。这个过程叫reassembles segments into messages, passes to app layer。
传输层的协议有TCP和UDP。
UDP(用户数据报协议)提供了一种不可靠的、无序的数据传输服务。它是一种"尽力而为"的协议,是对网络层(如IP协议)的一种简单扩展,适用于对数据传输实时性要求较高、可以容忍一定数据丢失的应用场景,比如音频、视频传输以及在线游戏等。
TCP(传输控制协议)提供了可靠的、按顺序的数据传输服务。它具有congestion control(拥塞控制)、flow control(流量控制)、connection setup(连接建立)和数据重传等功能,以确保数据的可靠传输和顺序交付。它无法提供延迟和带宽的保证,因此在某些特定的应用场景下可能不太适用。
传输层和网络层的区别:
网络层负责提供主机之间的逻辑通信,而传输层负责提供进程之间的逻辑通信。传输层依赖并增强了网络层提供的服务。
下面给出一个类比:
小白的房子里有10个小孩写信给小黑的房子里的10个小孩:
Hosts(主机)就好比是房子,代表了计算机或设备。Processes(进程)就好比是孩子,代表了计算机里运行的程序。App messages(应用信息)就好比是装在信封里的新建,代表了应用层传输的数据。Transport protocol(传输协议)就好比是小白和小黑们负责将信件分发给家里的小孩。Network-layer protocol(网络层协议)就好比是邮政服务。
下图展示了更多的传输层协议。
3.1 Multiplexing and demultiplexing(多路复用和多路分解)
Multiplexing at sender(发送端的多路复用)指的是处理来自多个套接字(sockets)的数据,并为每个数据添加传输层的头部信息,这些头部信息将在接收端被多路分解以正确地将接收到的数据段交付给相应的套接字。
Demultiplexing at receiver(接收端的多路分解)指的是多路分解是指利用传输层头部的信息来将接收到的数据段正确地交付给相应的套接字。
这套机制使得发送端能够同时处理来自多个套接字的数据,并在接收端能够正确地将数据段交付给相应的套接字,从而实现了端到端的数据传输和通信。
其中Demultiplexing的实现细节如下:当主机接收到IP数据报时,每个数据报都包含了源IP地址和目标IP地址。每个数据报携带着一个传输层的segment(数据段段)。每个传输层的段都包含了源端口号和目标端口号。主机利用这些IP地址和端口号来将接收到的段分发到合适的套接字上。
下图展示了,segment的格式,原本要传输的数据会被放置到数据段的application data部分,也就是payload部分。使用multiplexing,在数据前面加入头部信息,这些头部信息包含源端口号和目标端口号,以及其他头部信息。
UDP协议的Connectionless demultiplexing(无连接多路分解)的过程如下:
创建的套接字会有一个主机本地的端口号。
python
Socket = socket(AF_INET, SOCK_DGRAM)
Socket.bind(('', 12345))
当创建一个要发送到UDP套接字的数据报时,需要指定目标IP地址和目标端口号。这样发送的数据报将会被发送到指定的目标地址和端口。
python
clientSocket.sendto(msg,(server_name, server_port))
当主机接收到UDP数据报时,它会检查数据报中的目标端口号,并将UDP数据报传送到具有相应端口号的套接字。这意味着具有相同目标端口号的IP数据报,但来源IP地址和/或来源端口号不同的情况下,都会被传送到相同目标端口号的套接字上。
TCP协议的Connection-oriented demultiplexing(有连接多路分解)的过程如下:
每个连接都是通过四元组(源IP地址、源端口号、目标IP地址、目标端口号)来唯一标识的。
在接收端,当主机接收到TCP数据段时,它会使用这四个值来将数据段正确地分发到相应的套接字上。
对于服务器主机来说,它可能会支持许多同时存在的TCP套接字,每个套接字都由自己的四元组来标识。例如:Web服务器为每个连接的客户端都会创建一个独立的套接字,这样可以确保不同的客户端连接不会相互干扰。对于非持久连接的HTTP请求(non-persistent HTTP),每个请求都会使用不同的套接字,因此每个请求都会有自己的四元组来标识。这样可以确保每个请求都能够被正确地处理,而不会影响其他请求的处理。
下两张图展示了TCP的Connection-oriented demultiplexing(有连接多路分解),上面的服务器无法实现多线程,而下面的服务器实现了多线程因此可以同时处理多个请求。
3.2 Connectionless transport:UDP
UDP(User Datagram Protocol,用户数据协议),是一种简单直接的传输层协议,是一种"尽力而为"的协议,它不提供数据包的可靠交付,也不保证数据包的顺序到达。它是无连接的,所以在数据传输前不需要进行握手建立连接。每个UDP数据段都是独立处理的,不依赖于其他数据段的状态。
由于UDP的特性,它适合用于一些要求实时性和快速传输的应用,比如流媒体应用和域名系统(DNS)等。
尽管UDP本身不提供可靠性,但是在应用层可以通过特定的机制来实现基于UDP的可靠传输。比如在应用层可以添加错误恢复和重传机制来保证数据的可靠传输。
下图展示了UDP的segment format。
UDP报文头部每个字段都是16位,它包括发送数据包的端口号和接收数据包的端口号,以及UDP数据包的长度(包括头部和数据,用bytes表示),checksum(校验和)用于错误检测,application data前文介绍过就是需要传输的数据。
UDP的优点在于:
1.无需建立连接,这可以减少延迟。
2.简单:发送方和接收方没有连接状态。
3.UDP头部较小,这意味着在网络上传输时占用的带宽较少。
4.No congestion control(无拥塞控制):UDP不进行拥塞控制,它可以尽可能快地发送数据。
下图展示了Wireshark抓取的UDP数据包的具体情况。
UDP校验和的目标是在传输过程中检测UDP数据段中的任何错误。具体计算过程如下:将pseudo header(伪头部)、UDP头部和UDP数据的所有16位字节相加,如果结果有进位,则将进位加到最低位。然后将结果的每个位取反,得到最终的校验和值。发送端将计算出的校验和值放入UDP校验和字段中。在接收端,接收到的UDP数据段也会进行校验和的计算。如果计算出的校验和值与UDP数据段中的校验和字段值不相等,那说明有错误产生。如果相等,也可能存在错误。
其中伪头部并不是UDP数据包的一部分,而是用于计算校验和的附加信息。它包括:Source Address(源地址):发送方的IP地址。
Destination Address(目的地址):接收方的IP地址。
Protocol(协议):指示上层协议,对于UDP是17。
UDP Length(UDP长度):UDP头部和数据负载的总长度。
由于这里的IP地址是32位的,所以它会被拆解为两个16位的字节进行计算。计算例子如下:我们将2个16-bit整数相加。计算结果如下:
此时相加过程产生的进位超过了16位的最大值,进位会回到低位字节的高位,形成一个循环。这个技术称之为Wraparound。
结果如下:
将这里的最后结果的每个位结果取反,得到checksum值如下:
这个操作的python代码如下:
python
checksum = 0
while data != '':
checksum += int(data[:4], 16)
if checksum > 65565:
checksum &= 0xFFFF
checksum += 1
data = data[4:]
if len(data) == 2:
data += '00'
checksum = 65535 - checksum
print('%x' % checksum)
其中checksum += int(data[:4], 16)是 将每4个字节的十六进制数据转换为整数并加到校验和上。
如果校验和超过65535(16位最大值),则使用 &= 0xFFFF 进行环绕处理,并将进位加到校验和上。
data = data[4:] 移动数据指针,跳过已处理的4个字节。
如果数据长度是2的倍数,添加 '00' 以确保数据长度正确。
checksum = 65535 - checksum是将checksum的每一位取反。
3.3 Principles of reliable data transfer
可靠数据传输是指在网络通信中确保数据能够可靠地从发送端传输到接收端的过程,它再应用层、传输层、链路层都很重要,因此被列为网络领域中最重要的十大主题之一。
不可靠通道的特性,如丢包、乱序到达或损坏,将决定可靠数据传输协议(rdt)的复杂性。可靠数据传输协议需要实现额外的机制来确保数据的可靠传输,这包括错误检测、重传、确认和流量控制等。
下面将介绍可靠数据传输协议的开发:
逐步开发RDT协议的发送方和接收方。在开发初期,可能只关注数据从发送方到接收方的单向传输,但需要记住,控制信息(如确认、重传等)将在两个方向上流动,以确保数据的可靠传输。可以使用有限状态机来指定发送方和接收方。如下图所示。
RDT1.0指的是在一个已经是可靠的信道上进行可靠的数据传输。在这种情况下,底层信道是完全可靠的,不存在比特错误和数据包丢失的情况。其中,发送端和接收端各自拥有独立的有限状态机(FSM)。发送端将数据发送到底层可靠信道中,而接收端则从底层信道中读取数据。
RDT2.0指的是在可能存在比特错误的信道上进行可靠的数据传输。在这种情况下,底层信道可能会导致数据包中的比特发生翻转。为了检测比特错误,通常会使用校验和来进行校验。那如何从错误中恢复呢?为了解决这个问题,引入了ACKs(Acknowledgements,确认)和NAKs(Negative Acknowledgements,否定确认)概念。接收端会明确地告知发送端数据包是否接收正确,如果接收正确则发送ACK,如果发现错误则发送NAK。发送端在收到NAK时会重新发送数据包,以确保数据能够在可能存在比特错误的信道上可靠传输。
因此RDT2.0在1.0的基础上引入了错误检测和接收端回馈机制从而让2.0有更强的容错能力和可靠性。
下图展示了2.0的有限状态机。
看上去2.0机制已经很成熟了,但是其实还存在一个fatal flaw(致命缺陷):
如果ACK或NAK被损坏,发送端将无法得知接收端的情况。在这种情况下,发送端不能简单地进行重传,因为可能会导致重复的数据包。
为了解决这个问题,RDT2.1引入了处理重复数据包的机制:当发送端在收到ACK或NAK时发现其被损坏,它会重新发送当前的数据包,这时它会为每个数据包添加序列号,而接收端会discards(丢弃,即不向上交付)重复的数据包。
下图展示了RDT2.1的有限状态机。左边是发送端,右边是接收端。
对于发送端,初始状态:发送端调用 rdt_send(data) 函数开始发送数据。
创建数据包:使用 make_pkt(seqnum, data, checksum) 创建数据包,并通过 udt_send(sndpkt) 发送。这里的seqnum是数据包的序列号。
等待确认:等待来自接收端的 ACK 或 NAK 消息。
若收到 ACK,更新序列号并发送下一个数据包。
若收到 NAK 或收到损坏的数据包,重发当前数据包。
对于接收端,接收数据包:接收端调用 rdt_rcv(rcvpkt) 函数接收数据包。
校验与处理:校验数据包是否损坏及序列号是否正确。
若数据包无误且序列号正确,提取数据并传递给上层应用;同时发送 ACK 给发送端。
若数据包损坏或序列号不正确,发送 NAK 要求重传。
通常情况下,为了实现简单可靠的数据传输,发送端会为每个数据包添加一个序列号。使用序列号(0和1)可以满足这个需求,只使用两个序列号可以更容易地进行状态管理。此外,只使用两个序列号也意味着状态机需要的状态数量将减半,这样可以更容易地实现状态管理。发送端的状态机需要"记住"下一个期望接收的数据包应该具有的序列号是0还是1。
接收端在接收数据包时需要检查该数据包是否是重复的,因为发送端可能会重新发送相同的数据包。为了跟踪期望接收的数据包序列号,接收端的状态会指示下一个期望的数据包应该具有的序列号是0还是1。需要注意的是接收端无法知道其最后一次发送的ACK或NAK是否在发送端被正确接收。
下面我们介绍RDT2.2,它简化了协议设计和实现,从而提高了效率和可靠性。
它与RDT2.1具有相同的功能,但只使用ACK,而不使用NAK。接收端在接收到一个数据包后,不会发送NAK来指示发送端重传,而是直接发送ACK来确认上一个成功接收的数据包。此外,接收端在发送ACK时会明确地包含被确认的数据包的序列号。当发送端收到重复的ACK时,它会采取与收到NAK时相同的行动,重新发送当前的数据包。当接收方没有收到数据,发送方将等待一段时间来接收来自接收方的确认(ACK)。
下图展示了RDT2.2的有限状态机。
由于可能存在没有接收到数据的可能,即通道中数据包丢失。这种情况下,需要引入超时重传机制。发送端在发送数据包后会等待一个"合理"的时间来接收确认(ACK)。如果在这段时间内没有收到ACK,发送端会进行重传。如果数据包(或ACK)只是延迟而没有丢失,那么重传的数据包将会是重复的,但由于序列号的存在,接收端可以正确地处理这种重传。此外,接收端在发送ACK时需要明确指定被确认的数据包的序列号,以便发送端可以正确地进行重传。这里需要倒计时计时器(countdown timer)用于在发送端等待ACK的过程中计时。
下图展示了RTD3.0中发送端的有限状态机。
下图展示了无数据包丢失和数据包丢失的情况。
左边无数据包丢失:发送方发送数据包 pkt0 给接收方。接收方收到 pkt0 后,发送确认 ack0 回发送方。发送方收到 ack0 后,发送下一个数据包 pkt1。接收方收到 pkt1 后,发送确认 ack1 回发送方。发送方收到 ack1 后,发送下一个数据包 pkt0,这个过程会不断重复。
右边数据包丢失:发送方发送数据包 pkt0 给接收方。接收方收到 pkt0 后,发送确认 ack0 回发送方。发送方发送数据包 pkt1,但这个数据包在传输过程中丢失了。
由于发送方没有在预期时间内收到对 pkt1 的确认(ack1),因此触发了超时重发机制,然后重新发送 pkt1。接收方收到重新发送的 pkt1 后,发送确认 ack1 回发送方。发送方收到 ack1 后,继续发送下一个数据包 pkt0。接收方收到 pkt0 后,发送确认 ack0 回发送方。
下图展示了ACK丢失和超时过早/ACK延迟的情况。
ACK丢失:发送方发送数据包 pkt0 给接收方。接收方收到 pkt0 后,发送确认 ack0 回发送方。发送方发送数据包 pkt1,接收方收到后发送确认 ack1。但是,ack1 在传输过程中丢失了。发送方在等待 ack1 时触发了超时重发机制,然后重新发送 pkt1。接收方收到重新发送的 pkt1 后,检测到这是一个重复的数据包,因此它发送 ack1 而不是 ack0。发送方收到 ack1 后,继续发送下一个数据包 pkt0。
过早超时/延迟确认:发送方发送数据包 pkt0 给接收方。接收方收到 pkt0 后,发送确认 ack0 回发送方。发送方发送数据包 pkt1,接收方收到后发送确认 ack1。但是,ack1 延迟了,发送方在 ack1 到达之前触发了超时重发机制。发送方重新发送 pkt1。接收方收到重复的 pkt1 后,检测到这是一个重复的数据包,因此它再次发送 ack1。发送方收到 这个ack1 后,什么也不做,因为它已经收到了之前发送的 pkt1 的确认。在收到上个 ack1 的时候,发送方会继续发送下一个数据包 pkt0。
RDT3.0实现了可靠的数据传输,但是其性能可能很差。
例如:带宽为1Gbps,propagation delay(传播延迟)为15ms,数据包大小为8000bit。
D t r a n s = L / R = 8000 b i t s / 1 0 9 b i t s / s e c = 8 m i c r o s e c s D_{trans}=L/R=8000bits/10^9bits/sec=8 microsecs Dtrans=L/R=8000bits/109bits/sec=8microsecs
我们用 U s e n d e r U_{sender} Usender表示发送方忙于发送数据的时间比例。假设往返时间 R T T = 30 m s RTT=30ms RTT=30ms。
U s e n d e r = L / R / ( R T T + L / R ) = 0.008 / 30.008 = 0.00027 U_{sender}=L/R/(RTT+L/R)=0.008/30.008 = 0.00027 Usender=L/R/(RTT+L/R)=0.008/30.008=0.00027
由于往返时间 R T T = 30 m s RTT=30ms RTT=30ms,所以每 30 m s 30ms 30ms发送1KB数据包,这将导致链路上的吞吐量为33kB/秒。这与1Gbps的链路带宽相比,效率非常低。
即使物理链路具有很高的带宽,由于协议的效率问题,实际能够利用的带宽可能非常有限。
下图说明了更好地说明了上述地问题。发送方地利用率低,因为大部分时间在等待确认而非发送数据。这种问题在高延迟或低宽带的网络环境中更加严重。
3.4 Pipelined communication
为了解决前面说的协议效率低问题,我们现在使用pipelined protocols(流水线协议),允许发送方在等待确认的数据包时发送多个数据包。这意味着发送方可以在同一时间段内发送多个数据包,而不需要等待每个数据包的确认就能发送下一个数据包。因此为了实现流水线协议,需要增加序列号的范围,以便能够区分多个正在传输中的数据包。同时,发送方和/或接收方可能需要进行缓冲,以便确认哪些数据包需要重新传输。
下图展示了在pipelining(流水线技术)中,利用率得到了提升。
流水线协议的两种形式:Go-Back-N(回退N)和Selective repeat(选择性重传)。
Go-Back-N(GBN)协议中,发送方可以在流水线中同时拥有多达N个未确认的数据包。接收方只发送累积确认(cumulative ack),即确认收到的最后一个数据包。如果接收方收到的数据包中存在间隙,它不会发送确认,而是等待缺失的数据包重新发送。发送方会为最老的未确认数据包设置定时器,当定时器到期时,会重新传送所有未确认的数据包。
Selective Repeat(SR)协议中,发送方同样可以在流水线中同时拥有多达N个未确认的数据包。接收方会为每个收到的数据包单独发送确认。发送方为每个未确认的数据包维护一个定时器,当定时器到期时,只重新发送对应的未确认数据包,而不是重新发送所有未确认的数据包。
Go-Back-N(GBN)协议:
数据包头中有k位seq #(序列号),用于标识数据包的顺序。
发送方维护一个window(窗口),允许最多传输N个连续的未确认数据包。
当接收方发送ACK(n)时,表示确认了所有的数据包直到序列号为n的数据包,这被称为"cumulative ACK(累积确认)"。
发送方可能会收到重复的ACK,发送方将不会进行任何操作。
发送方为流水线中最老的未确认数据包设置一个定时器。
下图展示了GBN协议中的发送方的有限状态机。
下图展示了GBN协议中的接受方的有限状态机。
这里接收方只为按顺序接收到的具有最高顺序号的数据包发送确认。可能会产生重复的确认,但是这在GBN协议中是允许的,因为发送方对重复的确认不会进行任何操作。这里接收方只需要记住expectedseqnum(预期的序列号)。
对于接收到的乱序数据包,接收方会丢弃它们而不是进行缓存,因为接收方没有数据包缓存的功能。对于乱序数据包,接收方会重新发送对具有最高顺序号的数据包的确认(即上一个正确接受的数据包),以便通知发送方它已经接收到了哪些数据包。通过超时重传机制从而重新获取乱序数据包。
下图展示了刚刚说的过程的案例。
发送方的窗口大小N=4。发送方连续发送数据包0到3,其中 pkt2 数据包丢失,接收方正确接收到 pkt0 和 pkt1,并发送相应的确认 ack0 和 ack1。接收方收到 pkt3,但由于 pkt2 丢失,pkt3 是乱序的,因此接收方丢弃 pkt3 并重新发送 ack1,提示发送方重传 pkt2。在发送方接收到 ack0 和 ack1后,由于window中有空余,所以发送 pkt4 和 pkt5. 接收方收到 pkt4 和 pkt5,因为是乱序的,所以接收方丢弃 pkt4 和 pkt 5 继续重新发送 ack1。 当超时机制触发后,发送方重新发送 pkt2 到 pkt5 的全部数据包。
Selective Repeat协议:接收方会对所有正确接收到的数据包进行单独确认,并且可能会根据需要缓存数据包,以便在必要时将它们按顺序交付给上层应用。
对于每个未收到确认的数据包,发送方会设置一个定时器。
发送方维护一个Sender window(窗口),窗口的大小限制了已发送但未收到确认的数据包的序列号范围,确保了在任何时候发送方都不会发送超出窗口范围的数据包。
下图展示了SR协议刚开始的情况。
当接受方收到对应数据包,会向发送方发送ACK。
当发送方收到ACK,这个Window便会向后移动。
接着发送,如果遇到了数据包丢失的情况,如下图所示接收方未收到第二个数据包,但是已经收到了后续的两个数据包,此时这两个数据包虽然乱序,但是会被缓存保存并发送ACK。
这两个数据包的ACK也会被发送方接受。
由于超时重发机制,发送方会重新发送第二个数据包,当接收方接收到第二个数据包后,会发送ACK,并且第三个和第四个数据包也会被接受。
后续同理。
因此,对于发送方:
如果下一个可用的序列号在窗口内,发送方会发送该数据包。
当定时器超时(timeout(n))时,发送方会重新发送序列号为n的数据包,并重新启动定时器。
当接收方收到序列号为n的确认(ACK),其中n在[sendbase, sendbase+N]之间时:
1.将数据包n标记为已接收。
2.如果n是最小的未确认的数据包,发送方会将窗口基地址(window base)前进到下一个未确认的序列号。
对于接收方:
如果接收到的数据包的序列号在[rcvbase, rcvbase+N-1]之间,接收方会发送确认(ACK)给发送方,并根据情况进行以下操作:
1.对于乱序的数据包,接收方会将其缓存起来。
2.对于按顺序的数据包,接收方会将其交付给上层应用,并且还会将已经缓存的按顺序的数据包一并交付给上层应用,然后将窗口前进到下一个尚未收到的数据包。
如果接收到的数据包的序列号在[rcvbase-N, rcvbase-1]之间,接收方会发送确认(ACK)给发送方(该情况可能是发送方以为接收方没收到,所以不发送ACK,发送方会以为接收方还是没收到)。
对于其他情况,接收方会忽略该数据包。
下图展示了SR协议的案例。
发送方的窗口大小N=4。发送方连续发送数据包0到3,其中 pkt2 数据包丢失,接收方正确接收到 pkt0 和 pkt1,并发送相应的确认 ack0 和 ack1。接收方收到 pkt3,但由于 pkt2 丢失,pkt3 是乱序的,因此接收方将 pkt3 放入缓存并发送 ack3。发送方接收到 pkt0 和 pkt1 后窗口向后移动从而发送了 pkt4 和 pkt5。 接收方接收到 pkt4 和 pkt5 后,将 pkt4 和 pkt5 放入缓存并发送 ack4 和 ack5。当超时重发机制后, 发送方重新发送 pkt2。 接收方最终收到pkt2,将其与之前缓冲的pkt3、pkt4和pkt5一起交付给上层应用,并发送ack2。当ack2最终被发送方接受后,发送方将停止重传pkt2。
SR协议中的问题:
下图展示了这个问题,假设我们现在窗口大小为3,接收方可能因为序列号的范围问题从而无法区分新数据和重复数据。这里蓝色方块代表发送窗口,绿色方块代表接受窗口。
在情况(a)中,发送方发送了pkt0、pkt1和pkt2,接收方正确接收并发送了相应的ACK。当pkt3丢失后,发送方超时并重传pkt0,接收方能够识别出pkt0是重复的,因为它已经接收过序列号为0的数据包。
在情况(b)中,发送方发送了pkt0、pkt1和pkt2,接收方正确接收并发送了相应的ACK。当pkt2丢失后,发送方超时并重传pkt0。由于序列号的位数不足以区分不同的窗口周期,接收方无法识别pkt0是重复的,因此错误地将其当作新数据包接收。
为了避免这个问题序列号空间至少是窗口大小的两倍+1。
3.5 TCP: connection-oriented transport
TCP的特点和功能包括:
点对点传输:TCP是一种点对点的协议,即一个发送方和一个接收方之间的通信。
可靠的、按顺序的字节流传输:TCP提供可靠的数据传输,确保数据在传输过程中不会丢失、损坏或重复,同时保证数据的顺序性。
流水线传输:TCP使用拥塞控制和流量控制来设置窗口大小,从而实现数据的流水线传输,提高传输效率。
全双工数据传输:TCP支持双向数据流,允许在同一连接上进行双向通信。
面向连接:TCP在进行数据传输之前需要进行握手,即发送方和接收方需要交换控制消息来初始化连接状态,确保双方都准备好进行数据交换。
流量控制:TCP具有流量控制机制,发送方会根据接收方的处理能力来调整发送速率,防止发送方过载导致接收方无法处理。
TCP的数据段的结构如下:
TCP头部通常是32位(4字节)的倍数,包含源端口号和目标端口号, sequence number(序列号,用于标识从发送方发送的数据字节流的位置),acknowledgement number(确认号,用于告知发送方接收方期望接收的下一个字节的序列号),ACK(确认标志位,当ACK标志被设置时,确认号字段才有效)PSH(推送功能,当设置时,接收方应尽快将数据推送给应用程序),RST(重置连接),SYN(同步序列号,用于建立连接时同步两个节点的序列号),FIN(结束,用于终止一个连接),receive window(接受窗口,用于流量控制,指示接收方能够接受的字节数),校验和,Urg data pointer(紧急数据指针,指向紧急数据的结束位置),options (variable length)(选项字段,用于提供额外的TCP配置信息)、应用程序数据。
Sequence number(序列号):指数据流中第一个字节的编号。
Acknowledgement number(确认号):指接收方期望从对方接收的下一个字节的编号。Cumulative ACK(累计确认),意味着确认号表示接收方已经正确接收到的数据的最后一个字节的序列号。如果接收方正确接收了序列号为1到10的数据字节,那么它将发送一个确认号为11的ACK。这代表接收方已经成功接收了序列号为1到10的数据并且接收方期望下一个接收的数据字节的序列号是11。
在TCP协议中,没有具体规定接收方如何处理乱序到达的数据段,这取决于实现者的设计。通常,接收方会使用缓冲区来暂存乱序的数据段,直到它们可以按顺序排列。
下图展现了相关事例。
HostA将字符"C"发送给HostB,这个数据包包含序列号42,确认号79。HostB接收到字符"C"后会发送一个ACK给HostA并且HostB会将收到的字符"C"回显给HostA。这个数据包包含序列号79,确认号43(42+1)以及数据"C"。HostA接受到这个数据,发送一个ACK给HostB,因此这个确认数据包包含序列号43,确认号80(79+1)。
下图分别展示了从客户端发送的数据、服务器发送的确认、服务器发送的数据。
后两者都对前面客户端发送的数据进行了确认。所以它们的ACK都是原来数据的序列号1加上数据的长度7,即得到最后的ACK是8。
设置TCP的timeout value(超时值)是TCP的一个关键问题,因此首先超时值应该比RTT(round trip time往返时间)长。如果超时值设置得太短,可能会导致过早的超时重传,从而引起不必要的重传,增加网络负担。如果超时值设置得太长,可能会导致对丢失数据包的反应过慢,延迟了网络问题的发现和处理。
所以该如何正确评估RTT(往返时间)呢?我们使用SampleRTT(样本往返时间)来确定适当的timeout value(超时值),SampleRTT是指从发送数据段到接收到对应确认(ACK)的时间,这里忽略重新传输的时间。由于SampleRTT会变化,所以为了更好估计RTT时间,我们将近期的多次SampleRTT取平均值。
E s t i m a t e d R T T n = ( 1 − α ) × E s t i m a t e d R T T n − 1 + α × S a m p l e R T T EstimatedRTT_n=(1−α)×EstimatedRTT_{n−1} +α×SampleRTT EstimatedRTTn=(1−α)×EstimatedRTTn−1+α×SampleRTT
这里 E s t i m a t e d R T T n EstimatedRTT_n EstimatedRTTn是当前的估计往返时间, E s t i m a t e d R T T n − 1 EstimatedRTT_{n−1} EstimatedRTTn−1是上一次的估计往返时间, S a m p l e R T T SampleRTT SampleRTT是最近一次测量的实际往返时间, α α α是平滑因子,通常取值在 0 到 1 之间。这种计算方法给予最近的样本更大的权重,使得估计值对最近的变化更加敏感,过去的估计值对当前估计的影响会随着时间的推移而指数级减少。一般 α = 0.125 α=0.125 α=0.125。
首先,timeout interval(超时间隔)被定义为估算的往返时间(EstimatedRTT)加上一个"safety margin(安全边界)",这个安全边界是为了应对估算的往返时间的变化,以及网络中的不确定性。当估算的往返时间变化较大时,需要设置更大的安全边界。
其中Deviation(间隔)RTT计算公式如下: D e v R T T = ( 1 − β ) ∗ D e v R T T p r e v i o u s + β ∗ ∣ S a m p l e R T T − E s t i m a t e d R T T ∣ DevRTT = (1-β)*DevRTT_{previous} + β * ∣SampleRTT−EstimatedRTT| DevRTT=(1−β)∗DevRTTprevious+β∗∣SampleRTT−EstimatedRTT∣,通常这里 β = 0.25 β=0.25 β=0.25
所以超时间隔的计算公式如下: T i m e o u t I n t e r v a l = E s t i m a t e d R T T + 4 ∗ D e v R T T TimeoutInterval = EstimatedRTT + 4 * DevRTT TimeoutInterval=EstimatedRTT+4∗DevRTT,这里的 4 ∗ D e v R T T 4 * DevRTT 4∗DevRTT便是安全边界。推荐的初始 T i m e o u t I n t e r v a l TimeoutInterval TimeoutInterval值为1s。当出现超时后, T i m e o u t I n t e r v a l TimeoutInterval TimeoutInterval会加倍,一面过早出现超时,然而,只要出现更新的 E s t i m a t e d R T T EstimatedRTT EstimatedRTT,就使用上述公式再次计算 T i m e o u t I n e r v a l TimeoutInerval TimeoutInerval。
TCP在IP的不可靠服务之上创建了rdt(可靠数据传输)服务。它使用了pipelined segments(分段传输)技术提高传输效率,cumulative acks(累积确认)技术减少网络流量和提高效率,single retransmission timer(单一的重传计时器)技术确保数据的可靠传输。
数据的重传通常由两种事件触发:超时事件(timeout events):当发送方在超时时间内未收到确认时,会触发数据的重传。
重复确认(duplicate acks):当接收方收到重复的确认时,表明之前的数据可能丢失,发送方会触发数据的重传。
我们先考虑一个建议的TCP发送方,因此我们忽略重复确认、流量控制和拥塞控制。
当从应用程序接收到数据时,发送方会执行以下操作:
创建一个带有序列号的数据段,序列号表示数据段中第一个数据字节的字节流编号。
如果定时器尚未运行,则启动定时器。这里的定时器是为了跟踪最老的未确认的数据段。
超时时间间隔由TimeoutInterval决定。当定时器超时时,将会重传导致超时的数据段,并重新启动定时器。
当接收到确认时,发送方会执行以下操作:
如果确认确认了之前未确认的数据段,发送方将更新已确认的数据段范围。
如果仍有未确认的数据段,发送方会重新启动定时器,以确保及时处理未确认的数据段。
下图展示了这个简化TCP的有限状态机。
下图展示了超时重发机制事件。
左边是丢失ACK事件,HostB发送的ACK信息丢失了,HostA由于没有在超时事件内收到确认,所以HostA重新发送了一遍数据包。
右边是过早超时事件,HostA 向HostB发送一个序列号为92,包含8字节数据的 TCP 段,HostA继续发送更多数据,序列号为100,包含20字节数据。HostB接收到序列号为92和序列号为100的数据后,发送ACK=100和ACK120确认。然而ACK在传输过程中延迟了,导致HostA没有及时收到就触发了超时重发机制,重新发送了序列号为92,包含8字节的数据,HostB接受后发送ACK120表示自己已经累积确认到了100。
下图还列出了一种情况。
这里与上个情况不同的地方在于ACK100在传输中丢失,但是因为由于累计确认机制,接收方的ACK120表示成功接收到了所有知道序列号119的数据,因此不影响前面ACK100的丢失。
说完发送方,我们说接收方。接受方的事件和行为如下:
当按顺序到达的数据段的序列号与期望的序列号一致时,接收方会执行以下操作:如果已经确认了期望序列号之前的所有数据,则接收方可能会延迟发送ACK。它会等待一小段时间(最多500毫秒)以接收可能会在短时间内到达的其他数据段。如果在这段时间内没有收到下一个数据段,接收方将发送一个ACK。
如果有一个延迟的数据段等待确认,接收方会立即发送一个累积ACK,确认这两个按顺序到达的数据段。
当接收到的数据段是乱序的时,接收方会执行以下操作:如果接收到的数据段的序列号比期望的序列号高,接收方会立即发送一个重复的ACK,指示下一个期望的字节的序列号。
当接收到的数据段填补了之前的数据段中的空隙时,接收方会执行以下操作:
如果接收到的数据段部分或完全填补了空隙,且该数据段的起始位置是空隙的较低端,则接收方会立即发送一个ACK。
TCP的超时时间通常设置得相对较长,这样在发生数据包丢失时,会有较长的延迟才会重新发送丢失的数据包。
当发送方发送多个数据段时,如果某个数据段丢失,接收方将会发送重复的ACKs,指示它收到了乱序的数据段或者缺失的数据段。发送方可以通过检测这些重复的ACKs来判断是否有数据段丢失。
因此可以可以有一个快速的TCP重发机制,当发送方接收到三个重复的确认或者叫triple duplicate ACKs,发送方会立即重新发送最小序列号的未确认段,而不必等待超时时间。这种机制提高了TCP协议的可靠性和性能。
如下图所示,第二个数据包在传输中丢失,但是HostB成功接收到后续的数据包,所以这里后续的ACK都是100,从而触发了快速重发机制,从而在超时之前就对丢失的数据包进行了重发,提高了TCP协议的可靠性和性能。
TCP流量控制指接收方控制发送方的数据传输速度,以确保发送方不会以过快的速度发送数据,从而导致接收方的缓冲区溢出。
下图展示了接收方协议栈。
数据从发送方通过网络传输到接收方,首先通过 IP 层,然后是 TCP 层,最后到达应用层。应用程序可以从 TCP 套接字缓冲区中读取并处理数据。这个过程可能会比 TCP 接收数据的速度慢,这取决于应用程序的处理能力。如果应用程序处理数据的速度比 TCP 接收数据的速度慢,那么 TCP 套接字接收缓冲区可能会填满。这可能导致发送方的 TCP 发送缓冲区也开始填满,因为发送方会收到接收方的窗口大小减小的信号,这会限制发送方发送更多数据。
因此接收方通过在TCP报文头中的rwnd字段(receive window字段)来通知发送方可用的缓冲区空间。rwnd(receive window)代表了接收窗口的大小,也就是接收方缓冲区中的可用空间大小。接收缓冲区的大小可以通过套接字选项来设置,通常情况下默认大小是4096字节。许多操作系统会自动调整接收缓冲区的大小。
发送方会根据接收方通知的rwnd值(receive window值)来限制未确认的数据量(也称为"in-flight(在途)"数据),确保不会发送超过接收方缓冲区可用空间的数据量。这种流量控制机制保证了接收缓冲区不会溢出。
接下来的两张图便展示了这个窗口的实际大小。
在发送和接收数据之前,发送方和接收方进行"握手"来协商建立连接的过程。在这个握手过程中,发送方和接收方会相互确认对方愿意建立连接,并且就连接参数达成一致。
那两次握手可以解决这个问题吗?当然不行,由于可变延迟、消息丢失而重传、消息重排序、无法看到对方等原因所以两次握手存在问题。
如下图所示。
左边情景中,客户端发送请求连接请求,服务器同意后发送建立数据包。但是客户端在收到前触发了超时重发机制,从而又发送了一个请求连接。在这个请求连接发送到服务器之前。客户端已经关闭了,而服务器接收到这个后发的请求连接还是进行了处理,从而造成了一个半开放的连接(这里没有客户端)。
右边情景中,同样有一个半开放连接,并且有些数据进行了反复的传输。
因此,我们使用三次握手取解决这个问题 ,如下图所示。
客户端最初处于监听状态,等待连接请求。客户端选择一个初始序列号 x,并向服务器发送一个 TCP SYN(同步)消息,请求建立连接,这个状态叫SYN SENT。而另一边服务器一开始也处于监听状态,等待连接请求。当服务器收到客户端的 SYN 消息后,选择自己的初始序列号 y,并向客户端发送一个 TCP SYN-ACK 消息,表示同意建立连接并确认客户端的 SYN,这时服务器切换到SYN RCVD状态。客户端收到服务器的 SYN-ACK 响应后,发送一个 ACK 消息,此时连接建立完成,客户端进入已建立(ESTAB)状态。服务器收到客户端的 ACK 消息后,连接建立完成,服务器进入已建立(ESTAB)状态。
现实中情况如下:
下图展示了TCP三次握手的有限状态机图。
客户端从closed状态开始。发送SYN包后,状态变为SYN sent。发送 ACK 包后,状态变为 ESTAB。
服务器从从listen状态开始。收到客户端的SY 包并发送SYN-ACK包后,状态变为SYN rcvd。收到客户端的ACK包后,状态变为ESTAB。
TCP关闭时的情况要更为复杂,步骤如下:当客户端或服务器决定关闭连接时,它们会向对方发送一个TCP段,其中的FIN位设置为1,表示要关闭连接。接收方收到对方发送的带有FIN位的TCP段后,会向对方发送一个确认(ACK),以确认收到了对方的关闭请求。在收到对方的FIN并发送了确认后,接收方也会向对方发送一个带有FIN位的TCP段,表示自己也要关闭连接。
TCP协议可以处理双方同时发送关闭请求的情况,即两端同时发送带有FIN位的TCP段,这样双方都会收到对方的关闭请求并进行确认,最终完成连接的关闭。
下图展示了四次握手以关闭连接的过程。
客户端和服务器处于已建立状态,可以进行数据传输。客户端发送一个FIN包,此时客户端状态切换为FIN_WAIT_1状态,它不能再发送数据,但仍然可以接收数据。服务器收到客户端的 FIN 包后,发送一个 ACK 确认。此时服务器状态变为 CLOSE_WAIT,但服务器还可以发送数据。客户端收到服务器的 ACK 确认,确认了客户端的 FIN 包。此时客户端状态变为 FIN_WAIT_2,等待服务器发送它的 FIN 包。服务器完成数据传输后,发送一个 FIN 包,请求关闭连接,从而进入LAST_ACK状态。客户端收到服务器的 FIN 包后,发送一个 ACK 确认,此时客户端状态变为 TIMED_WAIT,等待足够的时间以确保服务器收到了 ACK 确认。这个状态也被称为 2MSL(两倍最大段生存时间)等待,以确保网络中没有遗留的重复数据包。服务器收到客户端的 ACK 确认后,关闭连接,进入 CLOSED 状态。经过 TIMED_WAIT 状态后,客户端关闭连接,进入 CLOSED 状态。
具体情况如下。
3.6 Principles of congestion control
Congestion(拥塞)是指在网络中存在过多的数据源以及数据发送速度过快,以至于网络无法及时处理这些数据的现象。这与流量控制是不同的,流量控制是在端到端通信中控制发送方发送数据的速率。
拥塞的表现形式包括丢包(路由器缓冲区溢出导致数据丢失)以及长时间的延迟(路由器缓冲队列中的排队导致数据长时间滞留)。
拥塞也是网络中最重要的十个问题之一。
下图描述了一个网络拥塞的情景。
这里有两个发送者和两个接收者,一个无限缓冲区的路由器,输出链路容量为R,无重传。
在这种情景下,由于两个发送者共享同一个输出链路,每个连接的最大吞吐量被限制为链路容量的一半(R/2)。
当两个发送者的总到达率接近链路的容量 R 时,由于缓冲区的无限性,数据包开始在路由器中排队等待,导致延迟显著增加。
下图描述了另一个情景。
这里路由器的缓冲区是有限的,发送方需要重传超时的数据包,应用层输入等于应用层输出,传输层输入包括重传。
理想情况下,发送方知道路由器缓冲区的状态,并仅在有空闲空间时发送数据。
在理想情况下,当输入速率小于链路容量 R 的一半(R/2)时,吞吐吐量与输入速率成正比,即 λ o u t = λ i n λ_out = λ_in λout=λin。当输入速率达到 R/2 时,吞吐吐量达到最大值 R/2,不再增加。
在这个情境下,我们再假设发送方能够确切知道哪些数据包在路由器中因为缓冲区满而被丢弃。当路由器的缓冲区满时,新到达的数据包可能会被丢弃。发送方不会盲目地重传数据,只有在确定数据包丢失时才会进行重传。
吞吐量图如下。
上面情景中还会有重复数据包。由于路由器的缓冲区满,数据包可能会丢失或被丢弃。发送方可能因为网络延迟或其他原因过早地认为数据包丢失,从而触发重传机制。发送方在过早超时后发送了两个副本的数据包,而这两个副本最终都被接收方接收。
吞吐量图如下:
当网络出现拥塞时,会导致数据包的丢失或延迟,发送方需要进行重传以确保数据的可靠传输。这就意味着对于同样的有效数据传输量,发送方需要进行更多的工作(例如重传),从而降低了实际的有效数据传输速率。此外,由于拥塞可能导致数据包在链路上出现多次传输,这也会导致不必要的数据重传,进一步降低了网络的实际有效的数据传输速率。
下图是另一个场景。
这里有四个发送者,数据通过多个路由器进行传输,允许超时重传。
当输入速率( λ i n λ_in λin)增加时,如果超过路由器缓冲区的处理能力,会导致数据包丢失。特别是当红色数据包增加时,所有到达上层队列的蓝色数据包都可能被丢弃,导致蓝色数据包的吞吐量降至0。
这便是网络拥塞中的其他花费:当网络出现拥塞导致数据包丢失时,任何用于传输该数据包的上游传输容量都被浪费了。
我们对于网络拥塞有两种不同的控制方法:
End-to-End Congestion Control(端到端拥塞控制):在这种方法中,网络层不提供对拥塞控制的直接支持,而是由传输层根据网络行为来进行推断和控制。在TCP协议中,发送方通过控制窗口的大小来进行拥塞控制。
Network-Assisted Congestion Control(网络辅助的拥塞控制):在这种方法中,路由器可以向发送方和/或接收方提供反馈(1-bit),以帮助控制拥塞。
TCP 拥塞控制采用的是加法增加、乘法减少(AIMD)的策略。
Additional increase(加法增加):发送方在每个往返时间(RTT)内,将拥塞窗口(cwnd,即允许发送的未被确认的数据段数量)增加1个最大段大小(MSS)。
Multiplicative decrease(乘法减少):当发生数据包丢失时,发送方将拥塞窗口减半,以作为拥塞信号,减少发送速率。
下图展示了这个策略下实现的拥塞控制。
在没有检测到网络拥塞的情况下,TCP 会逐渐增加其拥塞窗口的大小。当 TCP 检测到网络拥塞的迹象(如超时或收到三个重复的 ACK)时,它会将拥塞窗口大小减半。因此由于AIMD的特性,拥塞窗口的大小呈现出一种锯齿状的增长模式。
具体实现细节如下:
发送方的传输受到以下条件的限制: L a s t B y t e S e n t -- L a s t B y t e A c k e d < = m i n ( c w n d , r w n d ) LastByteSent -- LastByteAcked <= min(cwnd, rwnd) LastByteSent--LastByteAcked<=min(cwnd,rwnd)其中 LastByteSent 是发送方已发送但尚未确认的最后一个字节的序列号。
LastByteAcked 是接收方已确认的最后一个字节的序列号。cwnd 是拥塞窗口大小。rwnd 是接收方的接收窗口大小。cwnd是动态调整的,它根据网络拥塞的感知情况来变化。
TCP发送速率:发送方大致按照 cwnd 字节的数据量进行发送,然后等待一个往返时间(RTT)来接收 ACK,之后再次发送更多数据。因此公式 r a t e ≈ c w n d / R T T b y t e s / s e c rate ≈ cwnd / RTT \ bytes/sec rate≈cwnd/RTT bytes/sec
TCP的慢启动指的是在TCP连接刚开始时指数增加发送速率直到第一次丢失产生,以快速填充网络的带宽,同时避免引起网络拥塞。慢启动的主要特点包括:
初始cwnd(拥塞窗口)大小为1个最大段大小(MSS)。
每个往返时间(RTT)内对拥塞窗口大小进行指数增加,通过每次接收到一个确认(ACK)就增加拥塞窗口大小。
总的来说,慢启动阶段的初始发送速率较慢,但随着时间的推移,发送速率会以指数级别的速度增加,快速填充网络的带宽。
其中,MSS是一个动态值,默认情况下为536(计算得到,即576减去IP头部的20字节和TCP头部的20字节)。对于以太网(Ethernet)环境,MSS的最大值通常为1460(计算得到,即1500减去IP头部的20字节和TCP头部的20字节)。
当TCP检测到数据包丢失时,它可以采取以下几种策略来进行调整:
超时指示数据包丢失:当TCP检测到超时,即发送的数据包在预期时间内未收到确认,TCP将cwnd(拥塞窗口)设置为1个最大段大小(MSS),然后通过类似慢启动的指数增长方式将窗口大小快速增加到一个阈值,然后改为线性增长。
通过三次重复的ACK指示数据包丢失(TCP Reno):当TCP接收到三次重复的确认(ACK)时,表明有一个或多个数据包丢失。在这种情况下,TCP Reno算法会将拥塞窗口大小减半,然后改为线性增长。
TCP Tahoe算法总是在检测到数据包丢失后将拥塞窗口大小设置为1(无论是超时还是三次重复的ACK)。
那什么时候TCP应该从慢启动状态切换到避免拥塞状态呢?
当cwnd(拥塞窗口)的大小达到之前发生超时的一半时,TCP会将拥塞窗口的增长策略从指数级别切换为线性增长。这个阈值被称为ssthresh(慢启动门限)。一旦cwnd达到了ssthresh,TCP就会采用线性增长来调整拥塞窗口的大小。
下图便展示了上面说的切换以及两种不同策略。
下图展示了TCP的拥塞控制的有限状态机。
慢启动(Slow Start):
初始条件:cwnd = 1 MSS(最大段大小),ssthresh = 64 KB(通常的初始值),dupACKcount = 0(重复 ACK 的计数器)。
过程:每当收到一个新的 ACK 时,cwnd 翻倍,直到 cwnd 达到 ssthresh。
转换到拥塞避免:当 cwnd ≥ ssthresh 时,进入拥塞避免阶段。
拥塞避免(Congestion Avoidance):
条件:cwnd 已经达到或超过 ssthresh。
过程:cwnd 每个 RTT 增加 1 MSS,而不是翻倍,这是线性增长。
丢包事件:如果发生超时,ssthresh 被设置为 cwnd/2,cwnd 重置为 1 MSS,然后重新进入慢启动阶段。
快速恢复(Fast Recovery):
触发条件:接收到三个重复的 ACK,表明发生了丢包,但网络并未完全拥塞。
过程:
ssthresh 被设置为 cwnd/2。
cwnd 被设置为 ssthresh + 3 MSS(允许发送三个新的段加上丢失的段)。
每当收到一个新的 ACK 时,cwnd 增加 1 MSS,直到达到 ssthresh,然后恢复到拥塞避免阶段的线性增长。
下图展示了更多的TCP拥塞处理方法。
以上都是关于用传输层实现拥塞控制。
下面介绍网络辅助的拥塞控制。
Explicit Congestion Notification(ECN,显式拥塞通知机制):ECN通过在IP头部的服务类型(ToS)字段中使用两个比特来标记网络路由器指示的拥塞情况。
当网络路由器检测到拥塞时,它会通过设置这两个比特来向接收端指示网络拥塞的情况。
当接收端收到带有拥塞指示的IP数据报时,它会在回复给发送端的确认(ACK)段中设置ECE比特(显式拥塞标志),以通知发送端网络中存在拥塞。
下图展示了这一过程。
其中00:Not-ECT(非ECN-Capable传输)*,表示数据包所在的流不支持ECN。
01:ECT(ECN-Capable传输),表示数据包所在的流支持ECN,但当前数据包没有遇到拥塞。
10:ECT(ECN-Capable传输),与01相同,表示数据包所在的流支持ECN,但当前数据包没有遇到拥塞。
11:CE(拥塞经历),表示数据包所在的流支持ECN,并且在传输过程中遇到了拥塞。
下面我们看一下TCP average throughput(平均吞吐量)与window size(拥塞窗口大小)和往返时间(RTT)的关系。
我们忽略慢启动阶段以及假设总有数据要发送。
W 表示在发生丢包时的窗口大小。平均窗口大小(即在飞行中的字节数)是 W 的 3 / 4 3/4 3/4。这意味着在大多数时间内,拥塞窗口保持在 W 的 75 % 75\% 75% 左右。平均吞吐量是每个 RTT 的 3 / 4 W 3/4W 3/4W。这是因为在理想情况下,TCP 在每个 RTT 内可以发送接近其窗口大小的数据。
所以平均TCP吞吐量的计算公式为:
a v g T C P t h r o u g h p u t = 3 / 4 W / R T T b y t e s / s e c avg \ TCP \ throughput = 3/4 W/RTT \ bytes/sec avg TCP throughput=3/4W/RTT bytes/sec