何时适合使用UDP而不是TCP?

342

由于TCP能够保证数据包的传递,因此可以被认为是“可靠的”,而UDP并不保证任何东西,数据包可能会丢失。在应用程序中使用UDP传输数据相对于TCP流传输有什么优势?在什么情况下UDP会是更好的选择,为什么?

我假设UDP更快,因为它没有创建和维护流的开销,但如果一些数据永远无法到达目的地,那么这个假设是否还有意义呢?


63
除了可能遭受数据包丢失的问题外,UDP也不能保证您只会收到一次数据包。如果您的网络复杂或配置不良,可能会收到相同的数据包多次。提醒一下,因为人们往往会忘记这一点! - Brian Agnew
4
它甚至不能保证数据包的顺序。 - Mehrdad Afshari
22
TCP并不能保证数据一定会被发送成功,它只保证在数据传输成功的情况下,数据包的顺序与发送时一致。 - Chaim Geretz
8
顺便说一下,我经常看到人们把可靠性/有序传递等同于 TCP 重传。这些“专家”会告诉你,为了克服 UDP 上的传输错误,你将重新实现 TCP(糟糕的),因此你不妨使用 TCP。这是不正确的。除重传外,还有其他错误恢复技术,不会因小但非零误码率而遭受延迟或指数级降低吞吐量的影响。 - Ben Voigt
1
在Network Engineering Stack Exchange上曾经有一个非常类似的问题被提出:如何知道一个协议是使用UDP还是TCP?。我在这里提供链接到我的回答,以提供更多的见解。 - Eddie
显示剩余3条评论
24个回答

398

这是我最喜欢的问题之一。UDP经常被误解。

在你想要快速向另一台服务器获取简单答案的情况下,UDP表现最佳。通常情况下,你希望答案能够在一个响应数据包中返回,并且你准备实现自己的可靠性协议或重新发送机制。DNS就是这种情况的完美描述。(当然,DNS也支持TCP模式。)

另一个情况是在传输那些可以因为新数据的到来而替换上一批数据/状态的数据时,比如天气数据、视频流、股票行情服务(不用于实际交易)或者游戏数据。

还有一种情况是在管理海量状态时,你想避免使用TCP,因为操作系统无法处理那么多的会话。这种情况在今天很少见。事实上,现在有用户态TCP堆栈可供使用,这样应用程序编写人员就可以更精细地控制所需的TCP状态资源。在2003年之前,UDP确实是唯一的选择。

最后一种情况是用于组播流量。UDP可以同时向多个主机进行组播,而TCP则无法实现这一点。


感谢您提供有趣的答案。我们目前有一台服务器在使用UDP(高带宽要求),这是可以接受的,因为实际上只有一个跳(路由禁用...),但我们注意到在某些故障网络卡上,数据包重排序可能会成为一个问题。您建议使用哪种用户模式TCP(或其他用户模式流控)堆栈? - dashesy
@dashesy - 你能取消排序要求吗?负载中是否有单调递增的数字可用?如果有,你就不需要完整的用户空间TCP堆栈。 - drudru
@drudru- 是的,序列号是存在的,我可能需要自行缓冲和去抖动。谢谢,消除一个选项总是很好的。 - dashesy

73
如果一个 TCP 数据包丢失了,它将会被重新发送。这对于依赖实时特定顺序处理数据的应用程序来说并不方便。
例如视频流和特别是 VoIP (例如 Skype)。在这些情况下,丢失一个数据包并不是很重要:我们的感官并不完美,所以我们可能甚至都没有注意到。这就是为什么这些类型的应用程序使用 UDP 而不是 TCP。

27
我认为你弄反了。TCP会重新排列数据包,以便按发送顺序传递数据。而UDP不会重新排列数据包,会按接收顺序传递数据。 - Hans Malherbe
2
UDP不保证顺序,但是您可以对数据包进行编号并在检索后重新排序。 - Kugel
9
我认为我不得不反驳一下在Skype中未注意到丢包的说法 ;-) - Robert S. Barnes
6
请注意,你可能正在实现一个新的TCP协议栈。但是很难比操作系统做得更好。 - erikkallen
1
被未来事件所废弃的代码可以极大地提高性能。 - supercat
显示剩余2条评论

63

UDP的“不可靠性”只是一种形式,传输并非绝对保证。实际上,它们几乎总是能够传递。它们只是在超时后不被确认和重试。

TCP套接字协商和握手的开销非常大。真的非常大。UDP没有明显的开销。

最重要的是,你可以轻松地用一些可靠的交付握手来补充UDP,这比TCP的开销小得多。请阅读此文:http://en.wikipedia.org/wiki/Reliable_User_Datagram_Protocol

UDP在发布-订阅类型的应用中广泛用于广播信息。我IRC,TIBCO大量使用UDP来通知状态更改。

任何其他形式的单向“重要事件”或“日志记录”活动都可以使用UDP数据包很好地处理。你想发送通知而不构建整个套接字。你不期望各个侦听器的任何响应。

系统“心跳”或“我还活着”的消息也是一个不错的选择。错过一个不是危机。连续错过半打以上才是。


7
作为实际问题,它们几乎总能通过。这高度依赖于更低层网络的可靠性。 - m0skit0
此外,规划少量数据包丢失和过多数据包丢失之间是否有区别?丢失就是丢失,无论如何都必须进行规划。 - Sedat Kapanoglu

32

我从事的产品支持客户端和服务器之间UDP(IP)和TCP/IP通信,该产品最初使用IPX,15年前添加了IP支持,3或4年前添加了TCP/IP支持。瞎猜一下:UDP到TCP的代码比例可能约为80/20。这个产品是一个数据库服务器,因此可靠性至关重要。我们必须处理UDP带来的所有问题(数据包丢失、数据包重复、数据包顺序等),这些问题已经在其他答案中提到过。很少会有问题,但有时候确实会出现问题,因此必须加以处理。支持UDP的好处是我们能够根据自己的用途进行定制,并从中调整更多的性能。

每个网络都会不同,但UDP通信协议对于我们来说通常会快一些。怀疑论者将正确地质疑我们是否正确地实现了所有内容。另外,你还能从一个2位数的声望值得到什么呢?尽管如此,我刚刚出于好奇进行了一次测试。测试读取100万条记录(从某张表中选择*)。我将每个单独客户端请求返回的记录数设置为1、10和100(使用每种协议进行三次测试运行)。服务器距离我只有两个跳跃,而且连接的是100Mbit的局域网。在这个特定的测试中,数字似乎与其他人以前发现的一致(UDP在大多数情况下快约5%)。每种协议的总时间(以毫秒为单位)如下:

  1. 1条记录
    • IP: 390,760毫秒
    • TCP: 416,903毫秒
  2. 10条记录
    • IP: 91,707毫秒
    • TCP: 95,662毫秒
  3. 100条记录
    • IP: 29,664毫秒
    • TCP: 30,968毫秒

IP和TCP的总数据传输量大约相同。我们在UDP通信中有额外的开销,因为我们拥有一些与TCP/IP相同的东西(校验和、序列号等),这些在TCP/IP中是“免费”的。例如,Wireshark显示使用UDP请求下一个记录集的大小为80字节,而使用TCP为84字节。


如果你只是为了TCP开发,然后购买更好的硬件,而不是增加5倍的编码工作量,会怎样呢? - inf3rno
2
谢谢提供具体的数据!对于增加的复杂性而言,5%的改进有点令人失望。 - Sergei
1
可能这5%是因为它在本地网络中发送(两个跳点之间)?我猜测,距离越远,差异就越大。 - lepe

24

这里已经有很多好的答案,但我想补充一个非常重要的因素和摘要。通过正确调整,UDP可以实现更高的吞吐量,因为它不使用拥塞控制。TCP中的拥塞控制是非常重要的。它控制连接的速率和吞吐量,以尝试估计连接的当前容量,从而最小化网络拥塞。即使在非常可靠的链路上发送数据包(例如在核心网络中),路由器有限的缓冲区也会填满其容量并丢弃数据包,TCP通过缺少接收确认来注意到此丢包情况,并将连接速度降至容量估计值以下。 TCP还采用了称为“慢启动”的方法,但吞吐量(实际上是“拥塞窗口”)会缓慢增加,直到数据包被丢弃,然后再降低并缓慢增加直到数据包再次被丢弃等。这会导致TCP吞吐量波动。当你下载一个大文件时,你可以清楚地看到这一点。

因为UDP不使用拥塞控制,所以它可以更快且延迟更小,因为它不会寻求将缓冲区最大化到丢包点,即UDP数据包在缓冲区中停留的时间更短,并且以更少的延迟更快地到达目的地。由于UDP不使用拥塞控制,而TCP使用拥塞控制,因此它可以从TCP中夺取容量,以满足UDP流量。

但是,UDP仍然会受到拥塞和数据包丢失的影响,因此您的应用程序必须准备好以某种方式处理这些问题,通常使用重传或纠错码。

结果是UDP可以:

  • 实现比TCP更高的吞吐量,只要网络丢包率在应用程序可以处理的范围内。
  • 以更少的延迟比TCP更快地传递数据包。
  • 更快地建立连接,因为不需要初始握手来建立连接。
  • 传输多播数据包,而TCP必须使用多个连接。
  • UDP传输固定大小的数据包,而TCP以段方式传输数据。如果您传输了300字节的UDP数据包,则在另一端将接收300字节。对于TCP,您可能会向发送套接字提供300字节,但接收方仅读取100字节,您必须想办法知道还有200个字节正在传输中。如果应用程序传输的是固定大小的消息而不是字节流,则这一点很重要。
  • 总之,只要您还实现了适当的重传机制,UDP就可以用于与TCP相同类型的任何应用程序。UDP非常快,延迟较小,不受连接拥塞的影响,可以传输固定大小的数据报,并且可以用于多播。


    1
    当网络拥塞到足以导致数据包丢失时,TCP 会尽量减少对网络其他用户的影响,而许多基于 UDP 的实现则不会这样做。这使它们能够在逐渐减少的带宽中获得更大的份额,但也会降低可用的有用带宽总量(例如,在数据实际上可以传输但发送方没有意识到的情况下进行不必要的重传)。 - supercat
    首先,非常感谢您的出色回答,我从中学到了很多!但是我有一个问题:难道不是因为以太网适配器限制所有从第4层(TCP和UDP)接收到的数据包,所以分段发生在第3层(IP)吗?您是否指的是TCP中发生但在UDP中未发生的其他类型的分段?如果您能向我解释一下,我将不胜感激。 - ruslash
    1
    @Freezy。你说得对,超过链路(第二层)MTU的数据包的分段发生在第三层IP上。然而,TCP是一种基于流的协议,它将数据视为字节流。TCP会将其数据分段发送,以适应根据MSS大小调整的IP数据包,因此TCP也会进行分段。TCP放入一个片段中的数据量,或者套接字读取的数据量因许多因素而异;它可以是1个字节或MSS字节。对于UDP,如果数据包没有在途中丢失,接收方始终会获得发送方发送的确切字节数。 - Andy

    18

    UDP是一种无连接的协议,被用于像SNMPDNS这样的协议中,其中数据包可以无序到达,而数据包的即时传输是很重要的。

    在SNMP中使用UDP,因为网络管理通常需要在网络处于压力状态时进行,即在难以实现可靠的拥塞控制数据传输时进行。

    DNS中使用UDP,因为它不涉及连接建立,从而避免了连接建立延迟问题。

    干杯!


    15

    UDP具有较少的开销,并且适用于像流式传输实时数据(例如音频或视频)或在数据丢失情况下仍然可以接受的任何情况。


    11

    我知道的关于这个问题最好的答案来自 user zAy0LfpBZLC8mAC 在 Hacker News 上。这个答案非常好,我会按原样引用它。

    TCP 具有队列头阻塞,因为它保证完整和有序的传输,所以当数据包在传输过程中丢失时,它必须等待缺失数据包的重传。而 UDP 则会按照到达的顺序将数据包传递给应用程序,包括重复的数据包,并且没有任何保证数据包是否到达或者以哪种顺序到达(实质上只是 IP 加上端口号和 (可选) 负载校验和),但这对于如电话这样的应用程序来说是可以接受的。在这种情况下,丢失几毫秒的音频通常并不重要,而延迟非常令人厌烦,所以不需要重新传输,只需丢弃任何重复的数据包,将重排序的数据包排序到几百毫秒的抖动缓冲区中,并且如果数据包未及时或根本未出现,则会被简单地跳过,在支持编解码的情况下可能进行插值。
    此外,TCP 的一个主要部分是流量控制,以确保您获得尽可能多的吞吐量,但不会过载网络(这有点多余,因为过载的网络会丢弃您的数据包,这意味着您必须重新传输,这会影响吞吐量),UDP 没有这些功能,这对于如电话这样的应用程序是有意义的,因为具有给定编解码器的电话需要一定数量的带宽,您不能“减速”它,并且额外的带宽也不会使通话更快。
    除了实时/低延迟应用程序之外,UDP 还适用于非常小的事务,例如 DNS 查找,因为它没有 TCP 连接建立和拆除开销,无论是在延迟还是带宽使用方面都是如此。如果您的请求小于典型的 MTU,响应也可能是一样的,那么您可以在一个来回中完成,而无需在服务器上保留任何状态,并且流量控制以及排序等所有这些都可能对这种用途没有什么用处。
    然后,您可以使用 UDP 来构建自己的 TCP 替代方案,但是如果没有深入了解网络动态,这可能不是一个好主意,现代 TCP 算法非常复杂。
    此外,我想提到的是,还有 SCTP 和 DCCP 等协议,目前唯一的问题是(IPv4)互联网充满了 NAT 网关,这使得在终端用户应用程序中使用 UDP 和 TCP 之外的协议变得不可能。

    1
    您可以在UDP上运行SCTP和DCCP。 - Demi

    10

    视频流传输是使用UDP的完美例子。


    请提供一些例子。 - Gaurav Singh
    2
    “视频流媒体”是一个例子。考虑一场直播比赛正在Hotstar上进行流媒体传输。 - Sisir

    8
    UDP的开销较低,正如先前所述,适用于流媒体(如视频和音频)传输,在这种情况下只需丢失一个数据包而不是尝试重新发送和追赶就更好了。
    TCP没有提供传递的保证,你只是应该被告知套接字是否断开连接或基本上是否无法到达数据。否则,数据会在其到达时被发送。
    人们经常忘记的一件大事是UDP是基于数据包的,而TCP是基于字节流的,没有保证“TCP数据包”您发送的就是出现在另一端的数据包,它可以被分解成路由器和堆栈希望的许多数据包。因此,您的软件必须额外负担分析字节并将其转换为可用数据块的任务,这可能需要相当大的开销。UDP可能会乱序,因此您必须对数据包进行编号或使用其他机制对其进行重新排序(如果您想要这样做)。但是,如果您收到了该UDP数据包,则它以与离开时相同的顺序和所有相同的字节到达,没有变化。因此,“UDP数据包”这个术语很有意义,但“TCP数据包”未必正确。 TCP有其自己的重试和排序机制,这些机制对您的应用程序是隐藏的,您可以利用UDP重新发明它以适应您的需求。
    对于两端而言,UDP写代码要容易得多,基本上是因为你不必建立和维护点对点连接。我的问题通常是在哪些情况下您需要TCP开销?如果您采取捷径,例如假定接收到的TCP“数据包”是发送的完整数据包,那么您会更好吗?(如果您检查长度/内容,则可能会丢弃两个数据包)。

    TCP确实有交付保证:块A将在块B之前被传递到应用程序,因此如果应用程序在应用程序级别上确认(确认)块B,则知道它得到了块A。但这也发生在TCP处理级别。 - Simeon Pilgrim
    在TCP中,可以通过简单地在每个数据块前缀上其长度来安全地分隔数据块。根据应用程序的不同,可以使用固定的一字节、两字节或四字节长度前缀,或者可以使用N字节长度前缀来分隔大小为128^N或更小的每个数据块。这并不会带来太大的开销。这种设计在不保证无间隙按顺序传递的协议中可能会有问题,但是在使用TCP时,这种设计非常好。 - supercat
    如果要查找固定长度的数据,您甚至不需要长度,只需按字节计数即可。 - old_timer
    1
    @supercat。你说得完全正确。这也意味着你在应用程序中增加了复杂性;而这种复杂性实际上是UDP所需要的。因此,TCP更适合传输流,如文件。但当我想要块数据的可靠性,以及TCP之上的TLS的额外安全性时,我会像你一样去做。 - Andy

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接