65.9K
CodeProject 正在变化。 阅读更多。
Home

通过 UDP 协议进行实时通信

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (52投票s)

2011年11月7日

CPOL

27分钟阅读

viewsIcon

209630

介绍 UDP-RT 协议,实现实时系统中高效可靠的通信

介绍  

在几乎所有现代应用程序中,快速可靠的通信都是基本要求,但实时系统将其推向极致,要求网络提供实时响应。 在某些情况下,要求非常严格,可能需要特殊硬件才能达到所需的性能。 在过去十年中,新的技术不断涌现以支持不断增长的需求,但它们的价格昂贵且可用性低。 因此,大多数系统仍然使用基于以太网骨干网的 TCP/IP 协议栈。 此外,大多数程序员仍然使用众所周知的同步套接字 API 进行开发。 本文探讨了这些主导技术在实时系统中的使用,并展示了如何在此类系统中设计和开发通信。 特别是,它提出了一种基于 UDP 传输层的可靠协议,该协议的平均往返时间(RTT)不到一毫秒,并且网络中每个端口每秒处理数万条消息。 

背景  

实时网络不是一项简单的任务,所以在进一步深入之前,必须分析系统连接性要求。 正确的方法是映射和分类系统中的流量。 首先要进行的分类是消息大小及其延迟要求。 在许多情况下,这两个参数足以选择传输协议和网络拓扑。 

让我们从传输协议的选择开始。 由于本文重点关注 TCP/IP,因此我们的选择将在 TCP 和 UDP 之间进行。 下表提出了协议选择规则。

表 1(协议选择规则) 

延迟 / 消息长度

几 KB 或更少

几十 KB 或更多

1 毫秒(可能更少)到几十毫秒

UDP

?

几百毫秒或更多

TCP

TCP

上表中低延迟和大数据量单元格有一个问号。 在这种特殊情况下,我们应该考虑不同的方法,例如 InfiniBand™ 上的 RDMA,但这些技术不在本文的讨论范围之内。

TCP

长消息可能包含数据库表、图片、视频、大量参数以及其他类型的数据,这些数据必须在不影响系统性能的情况下在系统内移动。 TCP 协议在处理大量数据方面表现出色。 长消息允许 TCP 启动其引擎,并为滑动窗口找到最佳匹配,以避免重传并实现带宽的良好利用。 但随着延迟要求越来越严格,TCP 协议的弱点也暴露无遗。

首先,TCP 延迟是不确定的。 考虑一个有多个客户端的服务器。 客户端共享同一链路,因此它们的流量会相互影响延迟。 鉴于 TCP 协议缺乏公平性和服务质量特性,延迟变化可能超过数百毫秒。 此外,在多个客户端的情况下,带宽利用率可能会显著下降(在传统系统中可能下降到 50%)。

TCP 面临的另一个问题是缓慢的恢复过程。 丢失 TCP 片段的常规恢复可能需要数百毫秒。 有一些技巧可以缩短恢复时间。 例如,在 Windows XP™ 中,配置注册表中的 TcpAckFrequencyTcpDelAckTicks 参数可节省 200 毫秒的恢复时间。 不幸的是,这些设置并非在所有操作系统中都可用,即使可用,还有其他 TCP 定时器和算法会使恢复时间保持较高水平。 

短消息对 TCP 来说是一个特殊的挑战。 默认情况下,TCP 使用 Nagle 算法累积短消息并一起发送。 该算法可能会延迟数据包发送,从而影响其延迟。 套接字提供了一个 API 来禁用此行为(参见 setsockopt 函数文档),但旧版套接字实现可能没有此功能。

综上所述,TCP 是一种出色的协议,但不适用于实时通信。 如表 1 所述,它只能在延迟超过数百毫秒的系统中成功使用。 

UDP

实时系统中的短消息通常用于管理硬件设备、数据审计和闭环通信(根据设备实时产生的数据控制设备行为)。 由于数据的实时性,短消息必须在几毫秒内送达并到达目的地。 能够在这种延迟内工作的传输协议是 UDP。 实际上,观察消息从源的用户空间到目的地的用户空间并返回的往返时间(RTT)。 消息通过 UDP 协议的 RTT 高度依赖于硬件和通信对的操作系统。 尽管如此,即使是 100Mb 网络和非实时操作系统(例如 Window XP™),也能提供一毫秒的平均 RTT。 使用实时操作系统和更先进的网卡可以将其缩短到微秒。

当然,UDP 是一种不可靠的协议,所以要使用它,我们必须在应用层添加可靠性功能。 这是一项复杂的任务,但如果你有一个相当大的系统,你就应该实现 UDP 替代方案。 有许多基于 UDP 的协议,它们提供了可靠性功能(例如 RDP),但没有一个被发现适合异构系统中的实时通信。 本文提出了一种专门为此类系统设计的新型基于 UDP 的可靠协议。 

通常,当有人被问及网络需求时,答案会以带宽的形式给出。 这个答案对于实时系统来说是不够的。 正如我们已经看到的,对等方之间的流量可以根据其延迟要求和消息大小进行分类。 对于短消息,还有两个更重要的参数来描述流量:消息数量及其分布。 

考虑一个仅用于发送和接收短消息的 1Gb 链路。 理论上,1Gb 网卡应该能够每秒处理超过 2,000,000 个数据包(最小以太网帧大小为 64 字节),但实际上,这远不可行。 即使某些特定的网卡能够处理这么多数据包,在操作系统和应用程序中处理它们也将是一个真正的挑战。 例如,2 GHz 处理器最多为每个数据包提供 1000 个周期,这对于这项任务来说是一个非常紧张的数字。 

消息的负载分布也应考虑在内。 例如,如果多个客户端在向服务器发送数据包时高度同步,则服务器端丢失数据包的可能性更大。 这反过来又会导致数据包重传和延迟增加。

网络规划 

一旦我们完成了系统中流量的分类并选择了传输协议,我们就应该转到网络规划。 我们需要检查现有的网络拓扑(如果尚未存在则构建一个)并验证它是否能够处理流量要求。 通常,实时系统有一个专用的网络用于实时通信。 尽管如此,该网络可能在系统内的不同模块之间共享,并且这些模块可能具有不同的性能要求。 网络骨干必须足够强大,以处理所有模块所需的通信。 为了简化我们的讨论,我们假设系统中存在一个适当的网络骨干,我们的目标是解决其利用率问题。

网络规划的下一步是分离不同类型的流量。 例如,TCP 和 UDP 不应该混淆。 最好也将具有不同延迟要求的 UDP 通信分开。 通过分离流量,我们降低了密度,从而减少了数据包的冲突。 这也简化了网络分析。 当然,最好的分离是通过为每种流量类型创建专用的物理连接(独立的网卡和对等设备之间的独立线路)来实现的。 然而,在许多情况下,由于预算和硬件限制,这种分离是不可能的。 在这种情况下,我们可以尝试在时间上分离流量。 如果这也行不通,那么我们必须准确地对我们的通信进行基准测试,并根据发现,考虑实现一个专有协议来满足网络要求。 

UDP-RT 协议

UDP-RT 协议是一种基于 UDP 的实时通信协议,它允许以低延迟发送短消息并提供协议可靠性功能。 正式地,UDP-RT 协议应满足以下要求:

  1. 低延迟
  • 尽快将应用程序消息发送到其目的地
  • 尽快接收消息并将其传递给应用程序
  1. 可靠性 
  • 避免消息丢失
  • 如果消息丢失,检测并恢复它(恢复消息所需的时间与可靠性和低延迟要求相关,因此协议应特别关注此问题)
  • 处理消息乱序

除了上述功能要求外,保持协议简单也很重要。 实时系统中的模块可能运行嵌入式软件,这很难调试。 因此,为了设计和实现的简单性,我们有意放弃了一些高级功能。 尽管如此,对于那些对鲁棒性而不是简单性更感兴趣的人,我们将在文章末尾讨论协议的可能扩展。 

UDP-RT 套件分为三个主要模块:发送、接收和协议引擎(见图 1)。 前两个模块负责高效地发送和接收消息。 这些模块提供自定义能力,以优化宿主操作系统上的协议性能。 协议引擎涵盖了上述列表中的可靠性要求。 

图 1(UDP-RT 模块) 

UDPRT_1.gif

 

消息结构  

每个 UDP 数据报可能携带多个 UDP-RT 协议消息。 消息不能跨 UDP 数据报分割,因此最大消息大小受最大数据报大小(64KB)限制。 UDP-RT 消息由头部和有效载荷组成。 图 2 显示了消息头部布局。  

图 2(UDP-RT 消息头部布局) 

UDPRT_2.gif 

  • 版本 字段允许协议不断发展。 在未来的版本中可以添加新的功能,版本字段将帮助对等方确定它们的兼容性。 最新版本为 0.0。 
  • 类型:消息可以是 DATA(0x0)ACK(0x1)RESET(0x2) 类型。DATA 消息携带有效载荷。RESET 消息重置消息计数器。ACK 消息用于确认 DATA 和 RESET 消息的到达。RESET 和 ACK 消息不携带有效载荷。
  • 有效载荷长度 定义消息有效载荷中的字节数。
  • ID 是发送方分配给 DATA 和 RESET 消息的消息序列号。ACK 消息必须从相应的 DATA 和 RESET 消息中复制 ID。
  • 时间戳 包含 DATA 和 RESET 消息的发送时间,并由消息发送方填充。ACK 消息必须从相应的 DATA 和 RESET 消息中复制时间戳。 

通道设置 

一对使用 UDP-RT 协议的对等体被视为 UDP-RT 连接,并称为通道。每个通道都有自己的一组设置,这些设置定义了 UDP-RT 在通道上的行为。下面是可用通道配置设置的列表。 

  • 初始重传超时 – 重传超时的默认值。详情请参见“消息重传计时器”部分。
  • 重传超时模型 – 协议引擎用于计算重传超时值的算法。详情请参见“消息重传计时器”部分。
  • 最大消息延迟 – 应用程序允许消息通过的最大时间。详情请参见“消息重传计时器”部分。
  • 消息丢弃策略 – 定义如何处理“最大消息延迟”过期的消息。详情请参见“消息重传计时器”部分。
  • 乱序消息策略 – 定义是否允许通道上出现乱序消息。详情请参见“消息乱序”部分。 
  • 重置等待时间 – 重置序列期间的等待时间。详情请参见“重置消息编号”。 

协议引擎

丢失消息检测与恢复

这个想法非常简单。为了知道消息是否已到达目的地,它必须得到接收方的确认。发送方将消息存储在其内存中,只要它在等待确认。一旦收到确认,消息就可以被丢弃。如果消息在一段时间内未被确认,则必须重传。图 3 直观地展示了这一概念。 

图 3(消息重传)

UDPRT_3.gif  

 

当应用程序执行发送操作时,协议引擎执行以下操作。首先,它为消息分配 ID,并将其传递给发送模块,发送模块又将消息传输到目的地。然后,它调整消息的重传超时,并将其推入待确认消息队列(每个通道都有一个队列)。每当消息的重传超时过期时,协议引擎都会重传消息(将其交给发送模块)并为消息设置新的超时。如果收到确认,接收模块会通知协议引擎。协议引擎检查待确认消息,如果确认 ID 与其中一个匹配,它会从队列中删除消息,并通知应用程序消息已成功送达(如果应用程序请求此类通知)。图 4 显示了协议引擎中传出消息处理的流程图。 

图 4(发送消息流程图)

UDPRT_4.gif 

 

消息乱序  

接收模块将收到的消息传递给协议引擎。如果这是确认消息,则按照上一节所述进行处理。如果这是数据消息,则协议引擎必须确认它。因此,它生成确认消息并将其传递给发送模块。然后,它将收到的消息存储起来,直到应用程序请求。 

协议引擎可能由于重传和路由(在封闭的实时系统中,后者可能性较小)而收到乱序消息。因此,引擎将消息放入通道堆中,按消息 ID 排序。引擎知道接下来会收到什么消息,因此不应将乱序消息传递给应用程序,而应等待下一个有序消息。当然,有些应用程序可能不介意接收乱序消息,因此此行为是可配置的(乱序消息策略设置)。图 5 显示了协议引擎中传入消息处理的流程图。 

图 5(接收消息流程图)

UDPRT_5.gif  


消息重传定时器

计算定时器超时   

UDP-RT 协议将最大消息延迟定义为应用程序允许消息传递的时间。消息延迟是传输消息的实际时间;其值取决于网络硬件和对等操作系统行为。我们假设最大消息延迟显著大于平均消息延迟,并且有足够的时间进行重传。因此,重传定时器的超时值成为协议配置中最重要的参数。为了设置一个合理的重传超时,发送方计算平均消息 RTT。它将时间戳写入消息头,接收方将时间戳复制到相应的 ACK 消息中,这样发送方可以在收到确认时计算 RTT。给定平均 RTT,重传超时可以设置为 average(RTT)+ε,其中ε 是 RTT 抖动的补偿。由于消息 RTT 的分布可以认为是正态的,所以ε 可以设置为 2*σ,这涵盖了 97% 以上的 RTT。虽然平均 RTT 和ε 可以在协议工作期间计算,但初始重传超时必须由应用程序设置(初始重传超时设置),其值应通过基准测试或理论计算确定。我们将上面描述的超时模型称为平均 RTT 重传模型

只要通道数量相对较少且消息丢失在时间和通道上是偶发的,平均 RTT 重传模型就能很好地工作。实际上,如果某个随机消息丢失,协议将在 average(RTT)+ ε 超时后重传它。现在考虑一种情况,消息反复丢失。一旦它超过其最大消息延迟值,协议就不应尝试重传消息,而是根据消息丢弃策略设置进行处理。该设置允许应用程序为过期消息配置 UDP-RT 行为;协议可以悄悄丢弃这些消息,或者宣布系统故障。 

拥塞控制

随着数据包密度上升,平均 RTT 重传模型变得危险。考虑多个客户端向服务器发送消息。网络活动可能出现峰值,这可能导致大量消息丢失和延迟。结果,客户端将在重传计时器过期后尝试重传消息。如果负载尚未结束,重传的消息将在系统中创建更大的负载,因此将有更多消息丢失或延迟。最终,由于持续负载,某些消息可能会达到其最大消息延迟。为了处理这种情况,理解消息计时器过期是一个不太可能发生的事件,它是瞬时负载的结果至关重要。因此,为了防止网络泛洪,我们需要减轻网络的重传压力,让负载通过。这可以通过增加后续重传的重传超时来完成;例如,每次重传将超时增加一倍。我们将此超时模型称为指数 RTT 重传模型。请注意,UDP-RT 协议不具备处理系统中持续负载和像 TCP 那样调整消息发送速率的能力。相反,该协议假定系统在正常情况下应该没有问题地工作,并且只在发生事件后给系统一个恢复的机会。 

你可能会问,为什么不使用停-等方法并延迟消息发送,直到收到最近消息的确认。虽然这种技术可以防止网络溢出,但它也有自己的缺点。它可能会在消息发送中产生不必要的延迟,并且除非发送速率远小于平均 RTT 时间,否则延迟甚至可能累积。

QoS  

UDP-RT 协议不提供 QoS 机制。相反,它尽最大努力使消息传递时间的平均值低于最大消息延迟。只要没有消息丢失且 RTT 小于最大消息延迟,这当然是正确的。一旦消息丢失或延迟,重传机制就会开始工作,如果存在多个共享同一链路的通道,那么优先处理一个通道而不是另一个通道可能更合理(例如,某个通道的最大消息延迟比其他通道更严格)。换句话说,某些通道可能需要比其他通道更快的消息恢复。这可以通过为通道分配更激进的重传超时模型来实现。例如,为该通道分配平均 RTT 重传模型,而其余通道使用指数 RTT 重传模型。为了确定适当的超时模型,您可能需要对系统进行基准测试。我们将在协议扩展部分讨论更严格的 QoS 解决方案。  

重置消息编号 

默认情况下,发送方从零开始为 DATA 消息分配 ID,并按递增顺序为后续消息分配 ID。接收方将 ID 与消息一起传播到应用程序。应用程序可以随时将计数器重置为任何值。重置功能通常由应用程序用于启动新的处理序列或防止消息 ID 溢出。 

当应用程序请求重置 ID 计数器时,协议引擎会丢弃所有待确认的消息,并等待足够的时间,以允许系统清除已发送的消息。等待时间由重置等待时间通道设置定义。然后发送方发出 RESET 消息。接收方清除收到的消息堆,并将接收 ID 计数器重置为请求的值。RESET 消息以与 DATA 消息相同的方式进行确认。RESET 命令也会传播到接收应用程序,以便应用程序可以正确处理 RESET 通知。例如,它可以请求其协议引擎重置,从而重置给定通道上双向流量。重置序列的流程图如图 6 所示。 

图 6(RESET 流程图)  

UDPRT_6.gif 

 

接收和发送模块

接收和发送模块负责使 UDP-RT 协议适应宿主操作系统。这些模块在专用线程中运行并操作套接字。在本节中,我们将讨论这些模块实现的各个方面以及如何调整其性能。

套接字编程 

回想一下,该协议与同步套接字一起工作。同步套接字效率低下,但效率低下是为可移植性付出的代价。接收和发送模块主要使用三个套接字 API:selectrecvfromsento。实际上,接收器和发送器线程过程可以描述如下:  

Recever_Thread_Procedure() { 
      while(true) {
            select(FD_SET); 
            foreach(s in FD_SET) { 
                  recvfrom(message, addr);
                  notify_protocol_engine(message, addr);
            }
      }
} 

Sender_Thread_Procedure() {
      while(true) {
            wait_for_msg_from_protocol_engine(message, addr); 
            sendto(message, addr)
      } 
}   

让我们根据上面的伪代码检查套接字 API 的性能: 

select – 它通常是一个昂贵的 API,在某些条件下,它的执行可能需要几十毫秒。为了减少对 API 的调用次数,每个通道都有自己的套接字用于与其对等方通信,因此 select API 可以一次应用于大量套接字。 select 性能也可能受到同时调用它的线程数的影响。为了处理这个问题,接收模块允许定义接收线程的数量,并为每个线程分配自己的套接字组。    

recvfrom – 在 select 确定了带有数据报的套接字后,接收线程调用 recvfrom API 来检索它们。如果套接字缓冲区中有数据报等待,recvfrom 通常会立即返回。如果某些操作系统不是这种情况,那么 recvfrom 执行时间可能会成为 UDP-RT 协议的主要延迟限制。     

sendto – 此 API 通常很快,但在某些条件下其性能可能会下降。例如,在 Windows XP 中,从多个线程同时调用 sendto 可能会显著降低 API 性能(即使调用是在不同的套接字上执行的)。因此,发送模块提供了一种机制,用于定义发送线程的数量,并通过将消息合并到一个数据报中来减少 sendto 调用次数。  

需要考虑的另一个方面是套接字配置;例如,发送和接收缓冲区大小。如果发送缓冲区太小,无法包含所有未处理的数据包,则对套接字的下一个 sendto 操作将阻塞,直到缓冲区被释放。接收缓冲区也存在类似问题,如果接收套接字缓冲区已满,传入的数据包将被丢弃。正如我们上面提到的,每个通道都有自己的套接字,因此这些情况不太可能发生。尽管如此,如果缓冲区有问题,您可以调用 setsockopt API 来扩大它们。这样,您将允许更多未处理的数据包在缓冲区中堆积。 

接收器线程     

如上所述,接收模块允许运行多个接收线程。为了选择最佳的接收线程数量,应考虑许多参数:selectrecvfrom 性能、CPU 和核心数量、网络端口数量、通道数量以及通道的延迟要求。尽管处理此挑战的正确方法是系统基准测试,但仍有一些简单的指导原则。接收线程对多个套接字执行 select,然后逐一处理所有已信号的套接字,并将消息分派到协议引擎。消息检索和分派不应造成延迟,因此线程应有足够的 CPU 能力来完成这些任务。因此,作为起点,建议创建与给定计算机中 CPU 核心数量相同的接收线程。  

发送线程   

与接收模块类似,发送模块允许调整发送线程的数量。最佳发送线程数量应通过基准测试确定,但由于发送线程不执行需要 CPU 的处理,因此建议仅从一个线程开始。  

除了发送线程调优,该模块还应处理消息合并。每个发送线程管理一个用于发送请求的主队列,以及每个通道一个用于未决消息的队列。协议引擎直接将消息放入通道队列,并将发送请求推入主线程队列。每个发送请求都包含通道标识符,因此当发送线程从主队列中拉取请求时,它也知道从通道队列中拉取相应的消息。如果发送线程开始在发送中积累延迟,那么通道队列也会开始增长,因此当发送线程服务一个通道的请求时,该通道的队列中有多个消息,它会将它们合并到一个数据报中。通过这种方式,发送线程减少了对昂贵的 sendto API 的调用次数,并且还有机会在系统中的瞬时延迟之后追赶上来。请注意,发送模块只有在万不得已时才合并消息,以克服性能瓶颈,而不是像 TCP Nagle 算法那样作为抢占式操作。 

中断调节

UDP-RT 设计旨在解决的问题之一是网络中运行的大量数据包。即使协议引擎、接收和发送模块配置得当,大量数据包也可能对宿主操作系统造成严重问题。例如,在 Windows XP™ 中,每个 UDP 数据包在接收到数据包时都会触发中断,并且操作系统会创建延迟过程调用(DPC)。问题在于 Windows XP™ 在同一个核心上处理所有 DPC,因此在大量传入数据包的情况下,操作系统会成为数据包处理的严重瓶颈。幸运的是,现代网卡允许调节它们创建的中断。因此,如果您遇到 UDP-RT 速度变慢的情况,请分析中断性能(例如,通过运行 Thesycon® DPC Latency Checker),并在需要时使用中断调节。 

案例研究  

为了对 UDP-RT 协议进行基准测试,建立了一个特殊的设置。该设置模拟了客户端-服务器系统,由 700 多个配备 100MBit 以太网网卡的客户端、网络交换机和一台配备 2 颗 CPU(每颗 4 核)2.33GHz 的 Intel® 服务器组成。客户端通过几个级联交换机连接到服务器端的 6 块 1Gbit 网卡。设置示意图如图 7 所示。 

图 7(系统架构图)

 UDPRT_7.gif 


UDP-RT 性能可以通过消息的平均 RTT 来衡量。如果 RTT 显著小于最大消息延迟,则协议肯定能很好地服务系统。然而,有些情况下 RTT 参数并非决定性因素。RTT 可能很高,并且其值可能存在显著的抖动。应该检查另一个参数来衡量 UDP-RT 性能:待确认消息队列的平均长度。该参数对于高延迟系统特别有用,并能很好地衡量网络性能稳定性。  

服务器从客户端接收消息,对每条消息进行一些计算,然后将结果发送回去,因此相同数量的消息在两个方向上流动。这不包括 UDP-RT 为每条 DATA 消息发送的 ACK 消息。在我们特定的设置中,我们发现服务器到客户端方向没有消息丢失,因此 UDP-RT 已更改为不从客户端发送 ACK 消息。这样就降低了客户端到服务器方向的流量密度,在该方向上消息延迟和丢失经常发生。 

如果所有客户端每 3 毫秒发送一次消息,那么,总的来说,计算 ACK 消息在内,服务器每秒接收 130000 条消息并向客户端发送 260000 条消息。这相当于每 7.7 微秒接收一条消息,每 3.85 微秒发送一条消息。客户端发送的消息大小为以太网 MTU(1.5KB)。服务器发送的消息明显更小,不超过 100 字节。ACK 消息长度为 20 字节,因此通信所需的总带宽粗略估计为 2.2 Gbit,这不到服务器可用带宽(6 个 1Gbit 端口)的一半。  

UDP-RT 协议的性能已在服务器上安装的两个操作系统上进行了测试:Windows XP™ Professional 和 Windows 7™ Ultimate Edition。Windows XP™ 成功处理了 50000 条入站和 100000 条出站消息。入站消息的平均 RTT 为 10 毫秒,待确认消息队列的平均长度约为 7。客户端每秒重传几十条消息。出站消息的 RTT 和队列长度测量不可用,因为我们取消了客户端的 ACK 消息。Windows XP™ 配置的结果显示在下表中。  

表 2(UDP-RT 在 Windows XP™ 上的性能)

每秒入站消息数

平均 RTT (ms)

最大 RTT (ms)

平均消息队列长度

最大消息队列长度

50000

10

50

7

33

增加传入消息数量会导致 RTT 时间显著增加,并导致未确认消息队列溢出,该队列的大小限制为 50 条。此外,即使传入消息数量达到 50000 条,系统也极不稳定,并且测量值每隔几秒就会跳动。

必须提到的是,为了获得上述结果,已经进行了一些校准运行以调整 UDP-RT 接收和发送模块。在最终配置中,我们在所有网卡上使用了允许的最大中断调节,接收模块有 6 个接收线程,发送模块配置为只运行一个线程。  

然而,Windows 7™ 表现出了稳定的性能。系统成功处理了 130000 条传入消息和 260000 条传出消息,并且在几分钟的运行中,RTT 和队列长度测量值仅有轻微抖动。没有观察到消息重传。我们主要将这些结果归因于 Windows 7™ 的接收端缩放 (RSS) 技术。结果显示在下表中。 

表 3(UDP-RT 在 Windows 7™ 上的性能)  

每秒入站消息数

平均 RTT (ms)

最大 RTT (ms)  

平均消息队列长度 

最大消息队列长度 

130000

14

31

8 

9


Windows 7™ 的发送和接收模块配置与我们在 Windows XP™ 中使用的配置相同。进一步调整配置可能会带来更好的结果。

UDP-RT 的可能扩展

附带 ACK 消息

UDP-RT 最大的挑战是减少网络中运行的数据包数量。在上面的案例研究中,我们确定了网络中的可靠路径并豁免了相应的 ACK 消息。另一种更通用的方法是,将 ACK 消息附带在携带 DATA 消息的数据包上以减少数据包数量。为此,接收方在收到 DATA 消息后不会立即发送 ACK,而是延迟发送。如果需要将 DATA 消息传输到同一对等端,它将合并这些消息并将其发送到单个数据报中。接收方必须知道可以延迟发送确认多长时间。如果延迟时间过长,DATA 消息可能会被重传。为了克服这个问题,发送对等端应该告诉接收方是否允许延迟 ACK 消息以及延迟多长时间。这可以通过在 DATA 消息头中添加一个超时值来实现。此超时值应根据重传超时模型和最大消息延迟参数计算。例如,平均 RTT 重传模型没有给确认延迟留下太多空间,因此发送方必须将重传定时器增加一定的 δ,并将 δ 传递到 DATA 消息头中。然后接收方将等待最多 δ 时间以寻找附带机会。此外,接收方会将确切的 ACK 消息延迟时间添加到 ACK 头中,以便发送方可以从相应的 DATA 消息 RTT 中减去此时间。也可以使用其他 ACK 消息附带方案。

发送和接收模块中的 QoS 

前面几节已经提到,不同的消息恢复机制可以作为 UDP-RT 协议中 QoS 的基本实现。另一种方法是在接收和发送模块中实现严格的 QoS 模型。实际上,接收模块线程在套接字集上工作,因此可以通过将特定套接字放入单独的集合并比其他集合更频繁地采样来赋予它们更高的优先级。例如,采样可以通过加权循环算法完成。以类似的方式,发送模块中的发送请求队列可以分成几个队列,这些队列可以以不同的方式进行采样。  

在发送和接收模块中实现 QoS 并不能取代为不同通道分配不同恢复机制。由于底层操作系统和网络设备不支持 QoS,任何数据包都可能丢失或延迟,因此管理不同的恢复机制对于克服此限制至关重要。

总结

实时通信仍远未达到“即插即用”的程度。本文试图将 TCP/IP 协议的使用系统化,以适应实时通信,并提出了一种新的可靠协议,用于标准 TCP/IP 套件无法提供所需性能的情况。 

© . All rights reserved.