使用UDP的确认可靠性


16

我对UDP有疑问。对于上下文,我正在开发实时动作游戏。

我已经阅读了很多有关UDP和TCP之间的区别的文章,并且我对它们的理解非常好,但是其中有一件从未感觉过正确,那就是可靠性,尤其是致谢。我了解默认情况下UDP不提供可靠性(即数据包可能会被丢弃或乱序发送)。当需要某种可靠性时,我所见的解决方案(从概念上讲是有意义的)是使用确认(即服务器向客户端发送数据包,并且当客户端收到该消息时,它将确认发送回服务器)。 。

取消确认后会发生什么?

在上面的示例中(一台服务器向一个客户端发送数据包),服务器通过每帧重新发送数据包直到收到针对这些数据包的确认,来处理潜在的数据包丢失。您仍然可能会遇到带宽问题或消息混乱的情况,但是仅从丢包的角度来看,服务器已被覆盖。

但是,如果客户端发送了从未到达的确认,则服务器别无选择,只能最终停止发送该消息,如果需要该数据包中包含的信息,这可能会破坏游戏。您可以对服务器采取类似的方法(即一直发送确认,直到收到确认的确认?),但是这种方法会使您永远来回循环(因为您需要为确认而需要一个确认)等等)。

我觉得这里的基本逻辑是正确的,这给了我两个选择。

  1. 发送单个确认包,并希望最好。
  2. 发送少量确认数据包(可能是3-4个),并希望达到最佳状态,前提是并非所有数据包都将被丢弃。

这个问题有答案吗?我是从根本上误解了吗?是否可以保证使用我不知道的UDP?我对继续使用太多的网络代码感到犹豫,直到我对自己的逻辑是合理的感到满意为止。


11
也许您错过了“超时”和“重试”的概念。
Kromster表示支持Monica

我肯定会的。您是在暗示我的逻辑是正确的,并且听起来不是很消极,但是在进行网络编程时,我不能对几乎任何网络信息都承担任何保证?在实时游戏过程中,可能会丢失大量信息,这很好,但是我只是想确保自己了解问题。
Grimelios

10
完全没有保证。对。永远不要在算法中包含“希望”。他们必须处理任何不幸的组合。PS由于我们需要可靠的通信(用于锁步仿真),因此我们只是在RTS中切换了TCP,以解决一切麻烦。
Kromster表示支持Monica

5
如果需要可靠性,请使用TCP;不要紧时,请使用UDP。例如,玩家的坐标是通过UDP在我的游戏中发送的。我使用插值和平滑来平滑所有丢失的数据包。奇迹般有效。通过TCP发送确实需要可靠但可能会慢一些的东西。如果您有状态,而较新的状态会使旧状态无效,则UDP是一个不错的选择,因为介于两者之间的内容何时丢失8e。玩家位置)。
Polygnome

这不是您问题的直接答案,但我强烈建议您仅在绝对必要时(例如,在初始连接时)在实时游​​戏中要求确认。设计客户端和服务器,使它们“使用现有的东西”,直到可以的话,在无状态系统中获得新的数据包之前,要简单得多(而且更可靠)。Quake 3在基于快照的系统上做得非常好。同样,对于您真正需要的情况,例如Enet之类的库也只能可靠地发送某些数据包
jrh

Answers:


32

这是“ 两位将军问题”的一种形式,您是对的-重试次数不足以完全保证收据。

在游戏中的实践中,通常存在时间跨度,即使技术上可靠地获得信息,在此时间范围内信息也并不重要。就像发现您在2秒钟前排列了一张完美的头像一样,现在播放器现在不能使用该信息了。

如果您的数据包丢失率很高,以至于您无法在狭窄的反应窗口内例行获取所需的信息,那么对于实时游戏,您最好踢一下播放器并尝试在其他地方找到更好的匹配项,而不是继续尝试发送数据包以模拟可靠的连接。

因此,某些游戏复制系统完全跳过确认和重试,并选择尽可能多地仅对最新更新进行垃圾邮件发送。如果一个人掉落或迟到,那太糟了,那就跳过它,拿起下一个并继续,依靠预测和插值系统来消除差距,并使玩家可见的打player减到最小。

我突然想开始称其为“ Simba复制”,因为它无视过去的问题并试图活在当下。;)

拉菲基(Rafiki)对那种生活哲学做出了一些还原性的荒谬

一种混合解决方案是争先恐后地发送新更新,并且(因为游戏状态更新通常可能非常小/ 可压缩)还包含了最后一个更新,也许是之前的一个更新...所以以防万一客户端错过了它们,您不必等待整个往返时间就可以找到并修复它。大多数情况下,客户端已经看到了此消息,因此,这种方式会存在冗余数据,但是纠正丢失消息的等待时间较短。客户端的更新可以包括他们所看到的最新连续更新的索引号,因此您可以对下一个更新包中包含的旧更新数量保持最小的保守度。

您还可以将两层系统实现为另一种类型的混合系统,其中短期状态以不可靠的快速触发方式复制,并且长期状态使用TCP或您自己的可靠性实现方式(具有较高的重试性)可靠地同步。计数。但是,这变得更加复杂,因为您需要维护两个消息传递系统,并且两个快照可能彼此不同步,从而增加了一种全新的边缘情况。


1
+1,写得很好。我只想强调一点,这与动作/实时游戏更相关。TBS和RTS游戏(以及某些动作游戏的事件)在“时间跨度上,信息并不重要”的观点不同。
克罗姆斯特说,支持莫妮卡(Monica)

3
是的,对于回合制游戏,我想人们会使用TCP而不是尝试将自己的可靠性层放在UDP之上。;)我仍然将RTS中的微型设备归类为具有精确时间范围的游戏类型-这种混合方法可能在那里做得很好,因为在这种情况下,您既有低延迟更新,也有安全网来追溯处理重大的错过事件,例如资源支出。
DMGregory

这非常有帮助,并且可以验证我最初的关注。非常感谢你。
Grimelios

2
提及前向纠错可能也很有用。设计协议时,接收器可以独立地确定在接收到下一个数据包时丢弃了一个数据包,并添加了一些额外的数据来平滑所需的插值。这很有用,因为无论如何UDP数据包通常都不会充满,而您只是更频繁地发送较小的数据包以降低延迟。添加一些额外的字节不会损害延迟,并且在这些情况下带宽不是问题。
MSalters

@MSalters我要说的是,如果您愿意的话,值得自己详细说明。我赞成。:)
DMGregory

9

TCP使用的方法是,发送方将继续重新发送数据包,直到收到确认为止。接收者将忽略重复的数据包,但仍为它们发送确认。发件人将忽略重复的确认。

如您所知,如果数据包丢失,发送方将重新发送它。
如果确认丢失,发送方将重新发送原始数据包,这将导致接收方重新发送确认。

如果在一定时间内(可能是60秒或20次重试)未收到确认,则认为玩家已与游戏断开连接。您必须实施某种超时规则,否则拔下网络电缆的播放器将永远占用服务器上的资源。


TCP的一项基本功能是,发件人无需关心是否已确认任何特定的数据包,而大多数情况下则需要考虑“高水位标记”以及在没有高水位标记移动的情况下未处理多长时间的数据包。
超级猫

1
@supercat我不会说这很重要;更像是优化。
user253751

关于括号中的内容(向已收到的数据包发送ACK),我认为您实际上应该强调它而不是将其括起来。OP的理解(或至少是它的描述)似乎缺少它。
Angew不再为

@Angew现在完成了。
user253751

6

如果您想重新发明TCP,首先应看一下 TCP,它可以解决您所描述的确切问题(解决方案的一部分是使用用户定义的值进行重试和超时)。

使用2个通道(TCP通道(用于可靠通信)和UDP(用于低延迟通信))的解决方案并不少见。

一些解决方案检测客户端何时长时间丢失某些信息,然后启动重新同步,这可能使用UDP或TCP。

另一个常见的方法是设计通信,使其完全不依赖确认,但这超出了问题的范围。


3

在RTS中,您实际上不能使用TCP之类的协议,也不能使UDP可靠。如果尝试这样做,则每当网络出现问题时,游戏就会冻结。

相反,您可以设计协议,以使丢失的数据包无关紧要。

简短的版本是,你不关心其他的球员们最后一帧,只要你知道他们在哪里现在。长版本更加复杂。

问题就变成了,当数据包丢失时您该怎么办?答案是...你猜。玩家可能正在直线移动,对吗?沿此线向前移动一步即可。...除非没有RTS播放器一直沿直线移动。然后是碰撞检测。

这很难。许多游戏弄错了。可以说,对此没有正确的答案,只有各种错误可以权衡。

这些游戏运行良好的原因不仅在于他们对这些问题进行了漫长而艰苦的思考,而且还因为互联网变得相当可靠。实际上,几乎所有UDP数据包都及时到达目的地。(除非存在像防火墙这样的永久性问题)


魔兽争霸3使用TCP。
fsp
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.