未知网络编程(7):如何使不可靠的UDP可靠?

时间:2020-10-20

免费通讯软件


 1.前言

 在最近与实时音频和视频领域的许多朋友的交流中,已经谈到了可靠的UDP,这实际上是一个老式的问题。RUDP被用在许多著名的项目中,比如谷歌的QUIC和WebRTC。在UDP之上,它是可靠的。许多朋友认为它不可靠,一些朋友认为它是一个大杀手,可以解决实时领域的大多数问题。

 作为一家教育公司,雪巴君应用在许多实时场景中使用了统一描述编程技术来解决我们的问题,并且我们在不同的场景中采用了不同的统一描述编程方法。

 让我们看看我们使用RUDP的场景:

 1)延时250毫秒的全局实时1V1应答采用RUDP+多点中继智能路由方案;

 2)500 ms 1080P视频交互系统,采用RUDP+PROXY调度传输方案;

 3)6方实时同步写系统采用RUDP+重做日志的可靠传输技术;

 4)弱网络无线环境下的Pad 720 p传输系统采用RUDP +GCC实时流量控制技术;

 5)大规模直播的P2P分发系统通过RUDP+多点并行中继技术节省了75%以上的分发带宽。

 说到实时传输,我们将首先考虑RUDP,它应用于我们APP核心传输系统的所有方面。然而,我们已经在不同的系统场景中设计了不同的统一描述协议方法,所以基于这些热烈的讨论和我们的经验,我决定看看统一描述协议,与你们分享我的关于如何使统一描述协议可靠的实践经验。

 2.系列文章

 本文是系列文章中的第七篇,该系列文章的概要如下:

 未知网络编程(一):TCP协议中的疑难问题分析(上)

 未知网络编程(二):TCP协议中的疑难问题分析(下)

 未知的网络编程(3):为什么在关闭TCP连接时要等待时间和关闭等待时间

 未知网络编程(4):深入研究和分析TCP异常关机

 未知网络编程(5): UDP连接和负载平衡

 未知网络编程(ⅵ):深入理解UDP协议并充分利用它

 未知网络编程(七):如何使不可靠的UDP可靠?”(本条)

 未知网络编程(8):从数据传输层深度解密HTTP

 未知网络编程(九):理论联系实际,全方位理解域名系统

 如果你认为这一系列的文章太专业,你可以先阅读“懒惰网络编程导论”这一系列的文章。该系列的目录如下:

 惰性网络编程导论(一):网络通信协议的快速理解(上)

 惰性网络编程导论(二):网络通信协议的快速理解(下)

 网络编程中的懒人介绍(3):快速理解TCP协议就足够了

 网络编程中的懒人介绍(4):快速理解TCP和UDP的区别

 网络编程中的懒人入门(5):快速理解为什么UDP有时优于TCP

 3.参考

 TCP/IP的详细说明-第11章UDP:用户数据报协议

 为什么QQ使用UDP而不是TCP?》

 移动即时消息/推送系统的协议选择:UDP还是TCP?》

 简单介绍了TCP和UDP的区别

 UDP中数据包的最大大小是多少

 “为什么基于TCP的移动即时消息仍然需要心跳保持活跃?》

 4.UDP的三角形限制原则

 事实上,在实时通信领域存在一种三角平衡关系——成本、质量和延迟之间的约束关系:

 未知网络编程(7):如何使不可靠的UDP可靠?_aaa.png

 也就是说,在输入成本、获得质量和通信延迟之间存在一个三角约束(LEQ)关系,因此实时通信系统的设计者将在这三个约束下找到一个平衡点。TCP属于通过增加延迟和传输成本来保证质量的通信模式,而UDP属于通过牺牲质量来保证延迟和成本的通信模式,因此在某些特定情况下,RUDP更容易找到这样的平衡点。必须从可信赖的RUDP概念和使用场景来分析RUDP如何找到这个平衡点。

 5.实时通信中什么是“可靠的”

 在实时通信过程中,不同的需求场景对可靠性有不同的要求,我们在此概括三个定义:

 努力做到可靠:通信的接收者要求发送者的数据尽可能完整地到达,但是服务本身的数据可以被允许丢失。例如,音频和视频数据以及幂等状态数据。;

 无序和可靠:通信的接收者要求发送者的数据必须完全到达,但是到达的顺序可以忽略。例如:文件传输、白板书写、实时绘图数据、日志型附加数据等。;

 有序和可靠:通信的接收者要求发送者的数据必须完全按顺序到达。

 RUDP根据这三种类型的需求和上一节中的三角约束关系确定自己的通信模型和机制,即寻找通信的平衡点。

 6.为什么UDP应该是可靠的

 在这一点上,许多人可能会说:为什么麻烦,只是使用TCP!

 事实上,许多人也这样做。TCP是一种基于公平性的可靠通信协议。然而,在一些恶劣的网络条件下,TCP要么不能提供正常的通信质量保证,要么成本太高。在保证通信延迟和质量的情况下,尽可能地降低成本是我们应该对UDP进行可靠保证的原因。

 团结与民主联盟主要解决以下相关问题:

 端到端连接问题:

 通常,终端和终端之间的直接通信将涉及到NAT穿越。网络地址转换中的协议穿越非常困难,但是网络地址转换中的协议穿越相对简单。如果是端到端的可靠通信,一般由RUDP解决。场景有:端到端文件传输、实时音视频传输、交互式指令传输等。;

 弱网络环境下的传输问题;

 在一些无线或3G/4G移动网络中,需要低延迟的可靠通信。如果使用TCP,通信延迟可能会非常大,这会影响用户体验。例如,实时在线游戏通信、语音对话、多方白板书写等。,这些场景可以通过特殊的RUDP方法解决;

 带宽竞争问题:

 有时客户端数据上传需要突破TCP公平性的限制,实现高速、低延迟和稳定,也就是说,应该使用特殊的流量控制算法来挤压客户端上传的带宽,比如:直播音视频流,这样的场景不仅可以挤压带宽,还能更好的增加通信的稳定性,避免像TCP那样频繁的断开和重新连接;

 传输路径优化问题:

 在一些对延迟要求较高的情况下,应用层中继将用于优化传输路由,即动态智能路由。此时,双方使用RUDP进行传输,中间延迟用于优化中继路由的延迟。还有一个基于传输吞吐量的场景,例如服务之间的数据分发和数据备份等。这种情况一般采用多点并行中继来提高传输速度,这也是基于RUDP(这两点将在后面描述);

 资源优化问题:

 在某些情况下,为了避免TCP的三次握手和四波过程,RUDP被用来优化资源占用和响应时间,并提高系统的并发性,如QUIC。

 无论什么样的场景,都需要保证可靠性,即质量,那么如何在UDP上实现可靠性呢?答案是重传。

 7.重传模式

 IP协议在设计时并不是为数据的可靠到达而设计的,所以UDP依赖于重传来确保可靠性,这是我们通常的RUDP行为。

 在描述RUDP重传之前,首先要了解RUDP的基本框架,如图所示:

 未知网络编程(7):如何使不可靠的UDP可靠?_2.png

 RUDP分为发送端和接收端,每种RUDP在设计上都会有不同的选择和简化,在图中可以概括为一个单元。RUDP重传是指发送方通过在接收方反馈确认的丢包信息来重传数据,发送方根据场景设计自己的重传模式,可分为三种类型:定时重传、请求重传和前向纠错选择性重传。

 7.1预定重传

 定时重传很容易理解,也就是说,如果发送方在发送数据分组时(T1)经过一个RTO之后没有接收到数据分组的确认消息,则发送方重传数据分组。这种方法依赖于接收端的确认和实时操作,容易产生误判。有两种主要情况:

 1)另一方接收到数据包,但ack在发送过程中丢失;

 2)确认正在进行中,但发送方的时间已超过一个实时操作系统。

 因此,超时重传的方法主要集中在RTO的计算上。如果您的场景对延迟很敏感,但不需要高流量成本,那么RTO的计算可以设计得相对较小,以确保您的延迟尽可能小。

 例如,教育领域中的实时在线游戏和写作同步是典型的场景,其中延迟和质量被换取费用,这适用于小带宽和低延迟传输。如果是大带宽的实时传输,预定重传的带宽消耗非常大,在极端情况下,重传率为20%,因此大带宽模式一般采用请求重传模式。

 7.2请求重传

 请求重传是指接收端在发送确认消息时携带自身丢失消息的信息反馈,发送端在接收确认消息时根据丢包反馈重传消息。

 下图:

 未知网络编程(7):如何使不可靠的UDP可靠?_3.png

 在这个反馈过程中,最关键的一步是在发送回确认消息时应该携带哪些丢失消息的信息,因为在网络传输过程中,UDP会出现故障和抖动。接收器应评估通信期间网络的抖动时间,即rtt_var(RTT方差值),并记录发现数据包丢失时的时间t1。当t1+rtt_var < curr_t(当前时间)时,我们认为它丢失了。

 此时,后续的确认需要携带该丢包信息并更新丢包时间t2,然后继续扫描丢包队列。如果t2+RTO,

 这种方法是由丢包请求引起的重传。如果网络非常糟糕,接收端将持续发起重传请求,这将导致发送端持续重传,造成网络风暴并降低通信质量。因此,我们在发送端设计了一个拥塞控制模块来限制电流,这将在后面重点讨论。

 整个请求重传机制依赖于两个时间参数,抖动时间和RTO,这两个参数的评估和调整也与相应的传输场景密切相关。请求重传的延迟大于定时重传的延迟,一般适用于大带宽的传输场景,如视频、文件传输、数据同步等。

 7.3前向纠错选择性重传

 除了定时重传和请求重传的模式之外,另一种方式是通过FEC分组来选择重传。前向纠错(FEC)是一种前向纠错技术,一般通过类异或算法实现,也有多层前向纠错算法和猛禽弹簧码技术,实际上是一个解方程的过程。

 适用于RUDP的示意图如下:

 未知网络编程(7):如何使不可靠的UDP可靠?_4.png

 发送消息时,发送方将根据前向纠错方式对多条消息进行前向纠错分组,并通过异或方式获得多条冗余信息包,然后一起发送给接收方。如果接收端发现数据包丢失,但可以通过前向纠错分组算法恢复,则不会向发送端请求重传。如果数据包中的数据包不能被前向纠错功能恢复,它将向发送端请求原始数据包。

 前向纠错分组适用于解决需要时延敏感和随机丢包的传输场景。在带宽不足的传输条件下,前向纠错会增加冗余数据包,这可能会使网络变得更糟。前向纠错模式不仅可以与请求重传模式匹配,还可以与定时重传模式匹配。

 8.RTT和RTO的计算

 在上面介绍的重传模式中,RTT、RTO等时间度量被多次提及。RTT(往返时间)是网络环路延迟,环路延迟是通过发送数据包和接收确认数据包来计算的。示意图如下:

 未知网络编程(7):如何使不可靠的UDP可靠?_5.png

 RTT = T2 - T1:

 这种计算方法只计算某个消息时间的RTT,但网络会波动,不可避免地会导致噪声现象,因此在计算过程中引入了加权平均收敛的方法(详见RFC793)。

 SRTT = (α * SRTT) + (1-α)RTT:

 这样,我们可以得到近似的SRTT,它在公式中通常是α=0.8。下一步是计算RTT风险值(方差)。让我们设置RTT _ VAR = | SRTT-RTT |,然后srtt _ VAR =(α* srtt _ VAR)+(1-α)RTT _ VAR,这样我们就可以得到RTT

 但是最后,我们需要实时操作系统,因为当涉及到消息重传时,实时操作系统是一个消息重传周期。从网络的通信流中,我们可以很容易地知道在重传一个包之后,如果RTT+RTT_VAR之后的时间没有被确认,我们可以再次重传它,所以我们可以知道:RTO = SRTT+SRTT_VAR。

 然而,在严重抖动的情况下,一般网络仍有较大的重复率问题,因此RTO = β*(SRTT+RTT_VAR),1.2

 9.窗口和拥塞控制

 RUDP通过重传是可靠的,并且重传实际上在三角平衡关系中以费用和延迟来交换质量,所以重传将导致两个问题,一个是延迟,另一个是重传带宽,尤其是后者。如果控制不好,就会引发网络风暴,因此在发送端设计一个窗口拥塞机制,避免并发带宽被过度占用的问题。

 9.1窗口

 RUDP需要一个用于发送和接收的滑动窗口系统来配合相应的拥塞算法进行流量控制。有些舵需要发送和接收终端的窗口严格对应,而有些舵不需要发送和接收终端的窗口严格对应。如果涉及到可靠而有序的RUDP,接收者应该对窗口进行排序和缓冲。如果这是一个混乱和可靠的场景,或者尽最大努力做到可靠,接收器通常不缓冲窗口,而只是滑动位置。

 让我们先看看收发器窗口图:

 未知网络编程(7):如何使不可靠的UDP可靠?_6.png

 上图描述了发送方从发送窗口向接收方发送六条数据消息。当接收器接收到101、102、103和106时,它将首先判断消息的连续性,并将窗口的起始位置滑动到103。然后每个数据包都会对确认做出响应。当发送方收到确认消息时,它将确认消息的连续性,并将窗口滑动到103。发送方将再次判断窗口的空缺,并填写新的发送数据,即

 这里提到的值之一是接收终端接收106时的处理。如果它是有序和可靠的,106将不通知上层服务进行处理,而是等待104和105。如果这是尽力而为的可靠和无序的可靠场景,它将通知上层服务首先处理它。在接收到确认后,发送方的窗口应该滑动多少由它自己的拥塞机器决定,也就是说,窗口的滑动速度由拥塞机制控制,并且拥塞控制是基于分组丢失率或基于双方之间的通信延迟来实现的。这里有几个典型的拥塞控制。

 9.2经典拥塞算法

 TCP经典拥塞算法分为四个部分:慢启动、拥塞避免、拥塞处理和快速恢复。这四个部分旨在确定发送窗口和发送速度。实际上,它们是设计用来在当前网络条件下通过网络丢包来判断网络拥塞状态,从而确定更合适的发送和传输窗口。

 经典算法基于定时重传。如果RUDP使用该算法进行拥塞控制,一般情况是在考虑网络传输的公平性原则的同时确保有序和可靠的传输。让我们逐一解释这些部分。

 缓慢启动:

 当连接链路刚刚建立时,首先不可能将cwnd设置得很大,这将很容易导致大量的重传。在典型的拥塞情况下,cwnd在开始时将被设置为1,然后CWND将根据通信过程中的分组丢失而逐渐扩展,以适应当前的网络状态,直到它达到慢启动阈值(ssthresh)。步骤如下:

 1)初始化cwnd = 1并开始数据传输;

 2)如果收到确认,cwnd将增加1;

 13)当发送方在RTT之后没有发现任何分组丢失重传时,cwnd = cwnd * 2;;

 4)当cwnd >= ssthresh或丢包重传发生时,慢速开始结束,进入拥塞避免状态。

 拥塞避免:

 当通信连接结束并缓慢启动时,网络传输速度可能还没有上线。此时,需要通过缓慢的调整过程对其进行进一步调整。通常,如果在RTT之后没有发现分组丢失,cwnd = cwnd+1。一旦发现丢包和超时重传,就进入拥塞处理。

 拥堵处理:

 拥塞处理在TCP中非常激烈。如果发生丢包和重传,cwnd直接= cwnd/2,然后进入快速恢复状态。

 快速恢复:

 通过确认数据包仅在窗口的一个位置丢失,确定是否要快速恢复。如图6所示,如果仅丢失104,并且收到105和106,确认将总是将确认的基数设置为103。如果连续三次收到以103为基数的确认,它将快速恢复,即立即重传104。然后,如果接收到新的确认并且基数大于103,则cwnd = cwnd+1并且进入拥塞避免状态。

 经典的拥塞控制是基于丢包检测和定时重传模式设计的,这是一种典型的以延时换取质量的三角均衡关系。然而,由于其设计的公平性,很难压缩网络带宽,保证网络的大吞吐量和小延迟。

 9.3BBR拥塞算法

 谷歌设计了基于发送方时延和带宽评估的BBR拥塞控制算法,解决了经典拥塞算法的时延和带宽压缩问题。

 这种拥塞算法着重解决两个问题:

 1)充分利用具有一定丢包率的网络传输链路上的带宽;

 2)减少网络传输中的缓冲延迟。

 BBR的主要策略是定期返回确认和n ACK,以评估链路的最小和最大带宽。最大吞吐量(cwnd)为cwnd =最大带宽/最小rtt。

 传输模型如下:

 未知网络编程(7):如何使不可靠的UDP可靠?_7.png

 BBR的整个拥塞控制是一种检测带宽和速率的状态,它有四种状态:

 1)启动:启动状态(相当于缓慢启动),增益参数max _ gain = 2.85

 2)排放:满载传输状态;

 3)探测带宽:带宽评估状态,通过较小的BBR增益参数增加(1.25)或减少(0.75);

 4)探测_RTT:通过在延迟评估状态下保持最小传输窗口(4毫秒)进行RTT采样。

 那么这些状态是如何来回转换的呢?以下是BBR在QUIC的一般步骤:

 1)初始化连接时,初始cwnd = 8将被设置,状态将被设置为启动;;

 2)启动时发送数据,根据确认数据的采样周期判断带宽是否可以增加,如果可以,设置cwnd = cwnd *max_gain。如果时间段的数量超过预设的开始时间段时间或发生分组丢失,则执行“排空”状态;

 3)在排空状态下,如果航班大小> cwnd,继续确保排空状态,如果航班大小

 14)在PROBE_BW状态下,如果没有分组丢失和飞行大小cwnd,cwnd = cwnd * 1.25如果有数据包丢失,cwnd = cwnd * 0.75;

 5)在启动/排放/探测_带宽三种状态下,如果通信中没有RTT持续10秒,

 6)在PROBE_RTT状态下,它将持续判断航班大小> = cwnd,并且当确认返回时没有数据包丢失,并将这次计数的最小RTT数作为min_rtt,进入启动状态。

 BBR通过上述步骤周期性地计算cwnd,即网络的最大吞吐量和最小时延,然后通过调步速率确定此时发送方的码率,最终达到拥塞控制的目的。

 BBR适合随机丢包和网络稳定情况下的拥塞。如果网络信号在无线或4G网络上非常不稳定,很容易出现网络泛滥和预测不准确的问题。在多连接公平性方面,BBR也存在与小RTT的连接比与大RTT的连接消耗更多带宽的情况,这很容易导致与大RTT的连接太慢。BBR拥塞算法是一种在三角均衡关系中用费用来交换延迟和质量的情况。

 9.4电子广播GCC

 说到实时音频和视频传输,我们必须想到开源的实时音频和视频工程WebRTC。在网络视频传输中,还实现了一种用于视频传输的拥塞控制算法。WebRTC的GCC是一种基于发送端丢包率和接收端延迟带宽统计的拥塞控制,是一种可靠的传输算法。如果消息在传输过程中被多次转发,它将被直接丢弃,这符合视频传输的场景(单击此处查看更多WebRTC文章)。

 借一张照片看看发生了什么:

 未知网络编程(7):如何使不可靠的UDP可靠?_9.png

 (这张图片来自文章“基于GCC的网络广播中心拥塞控制(一)——算法分析”)

 GCC的发送端将根据丢包率和比较表调整速率。当损耗小于2%时,它将增加传输带宽。当损耗> =2% &&loss =10%时,它将考虑传输过载并降低传输带宽。

 接收端通过卡尔曼滤波器根据数据到达时延的方差和大小进行带宽近似收敛。细节没有介绍。请检查基于KalmanFilter的WebRTC视频接收缓冲器的延迟模型。

 这里值得一提的是,GCC引入了接收机来评估KalmanFilter的带宽,这是一种非常新颖的拥塞控制思想。这对实现一个可靠的RUDP传输系统是一个很好的参考。

 然而,该算法也存在一个缺陷,即在网络中出现间歇性丢包的情况下,GCC可能收敛缓慢,这可能使得REMB在一定程度上难以反馈给发送方,并且容易导致发送方的流量控制失败。GCC是一种在三角平衡关系中以质量和费用换取延迟的情况。

 9.5弱窗口拥塞机制

 事实上,在许多情况下,不需要拥塞控制或者只需要弱拥塞控制。例如,教师和学生编写同步和实时游戏,因为传输的数据量很小,只要延迟和可靠性足够小,固定窗口大小通常用于流量控制。在系统中,我们一般使用cwnd =32这样的窗口进行流量控制,同时通过整个接收窗口的数据状态将确认信息反馈给发送方,简单直接,可以很容易地适应弱网络环境。

 10.传输路径

 RUDP不仅优化了连接,压缩了带宽,适应了脆弱的网络环境,而且继承了UDP的自然动态,可以优化中间应用层链路上的传输,一般分为多点串行优化和多点并行优化。让我们具体谈谈。

 10.1多点串联继电器

 在实时通信中,一些业务场景对延迟非常敏感,如实时语音、同步书写、实时交互、直播等。如果仅仅是服务传输或P2P通信,很难满足他们的需求,特别是当物理距离非常大时。

 为了解决这个问题,SKYPE率先提出了全球实时多点传输网络(RTN),即通过几个中继节点在通信双方之间动态、智能地路由。这种传输模式非常适合于RUDP。我们只需要在两个通信方之间建立一个RUDP通道,中间链路只是一个无状态的中继缓存集。中继之间进行路由检测和路由,以实现高可用性和实时链路。

 下图:

 未知网络编程(7):如何使不可靠的UDP可靠?_10.png

 RUDP的传输优化是通过多点中继来保证的,这是一个典型的三角形均衡关系中以代价换取延迟的情况。

 10.2多点并联继电器

 在媒体数据在服务之间传输或分发的过程中,需要保证传输路径和并发带宽的高可用性。在这种使用场景中,传输双方将被用来建立一个RUDP信道,该信道通过并联多个中继节点来解决。

 如下图所示:

 未知网络编程(7):如何使不可靠的UDP可靠?_11.png

 该模型需要在发送端设计一个多点路由表检测机制来判断每条路径同时发送的数据的比率和可用性。除了链路备份和增加传输并发带宽外,该模型还具有辅助功能。如果是流媒体分发系统,我们通常使用BGP作为中转。如果节点可以直接连接,还可以减少对BGP带宽的占用,从而降低成本。

 11.本文摘要

 RUDP的介绍在这里,它是一篇初级的科普文章,通过谈论一些细节和场景。自团结和民主联盟的概念提出以来,已有近20年的历史。许多实践者希望通过一套完美的方案来设计一个通用的RUDP。我个人认为这不太可能。即使是设计好的,现在估计也和TCP差不多,所以没什么意义。

 RUDP的价值在于根据不同的传输场景选择不同的技术,可以选择松散拥塞模式或特定的重传模式,但无论如何选择,它都是基于成本、延迟和质量之间的权衡。RUDP可以通过组合场景和平衡三角平衡关系来帮助开发人员找到更好的解决方案。

 12.这篇文章的作者