我的服务器应该同时使用TCP和UDP吗?

12

我正在编写一个客户端/服务器应用程序,但找不到适合我的指南。自己做往往会在开始之前就出现许多设计缺陷。例如,服务器应该每秒更新每个客户端的状态多次。我认为我不能单独更新每个客户端,但UDP广播应该可以解决这个问题。但是我仍然需要一个TCP通道,以可靠地与每个客户端进行通信,例如:聊天消息、用户输入等。

研究这个主题,似乎服务器可以同时使用两种协议 - 但只有可能(并不明智)。没有人建议这种方法,事实上,我从这篇文章中得知,同时使用两种协议会导致一些问题。


现在我非常困惑应该如何处理服务器上的数据。除非我完全误解了数据包丢失的情况,我希望保证用户输入产生的“服务器请求数据包”不会丢失。关于使用UDP保证交付的每个SO答案都说要使用TCP,更令人沮丧的是,我能想象到的每个服务器/客户端程序都至少需要一些消息被保证交付(例如“断开连接”消息?)。

我应该使用UDP、TCP、两者都用还是我完全错了?


1
我只是想指出Kyronet https://github.com/EsotericSoftware/kryonet,它是一个用于游戏的库,使用tcp和udp,并使其变得更加简单。Enet http://enet.bespin.org/是一个库,用于在udp上实现一层,允许您同时使用相同的套接字发送可靠/不可靠数据。如果您查看,还有一个Java实现。我没有使用过这些,所以无法提供任何帮助。两种方法都没有问题。 - WalterM
6个回答

16

让我们先列出一些事实:

  • UDP不可靠,从来都不是。

  • 在某些情况下,UDP可能会丢失数据,例如网络拥塞、速率限制或流量分析,或者如果UDP消息大小大于MTU。

  • UDP广播仅适用于您的本地网络段。因特网路由器通常不允许广播传播得更远。这真的限制了其有用性。

  • UDP组播可能是一个选择,但往往也会被阻止。

因此,这可能只留下了两个主要选择:

  • 从服务器到每个客户端使用UDP点对点通信。
  • 从服务器到每个客户端使用TCP。

另一个可能性是使用UDP或TCP的某种对等网格通信,但这会变得非常复杂。除非您确实需要,并且确实知道自己在做什么,否则不要去那里。

所以回答您的问题:

我应该使用UDP、TCP、两者还是我的想法完全错误?

我建议在服务器和每个客户端之间使用TCP,因为它最简单。为了进一步简化,每个客户端使用多个TCP连接,以避免在单个套接字上复用多个“对话”的复杂性。

网络性能可能不是最优的,但对于您的应用程序而言,可能足够好。我想这不是您想要花费所有开发人员时间的应用程序部分。

当您拥有工作应用程序(客户端和服务器端)并且有人在使用它时,您可能会发现(或者不会!)网络是用户不满意的主要瓶颈和原因。那时候您可以考虑在应用程序的第二个版本中优化通信。

当您开始实现2.0(或3.0……)版本以解决可扩展性问题时,您需要摆脱对单个服务器的依赖。简单地说,如果您有N个客户端,并且N不断增加,那么在某个点上,单个服务器将无法处理。应用程序设计中的其他问题也可能会带来麻烦。例如,在游戏应用程序中,您无法向所有其他玩家发送有关每个玩家的持续更新……而玩家数量不断增加。但请注意,这些问题在很大程度上与您所使用的网络协议无关。


2
坦白地说,如果你愿意花时间去做,那就去做吧。尝试所有的替代方案,并进行扩展实验,这将告诉你哪个是最好的。我们无法为你提供关于哪种替代方案最适合你的详细可靠建议,因为它取决于一大堆变量... - Stephen C
1
“但我必须坚持认为有一种通用的正确方法。” - 不正确。有很多可能是最佳方式的方法,但这实际上取决于应用程序的细节...以及您对“最佳”的标准是什么。 - Stephen C
我必须盲目地假设我在某些方面完全错了。否则,互联网上会有我正在寻找的信息。虽然我不知道自己哪里出了问题。我可以按照自己的意愿去做:同时使用两个协议,这将解决我所有的问题,并且我可能不会感到自己做错了什么。 - user2651804
我断言几乎所有的服务器/客户端应用程序都需要至少可靠地交换一些信息。这个说法不正确吗? - user2651804
1
为了进一步简化,使用多个TCP连接来避免在单个套接字上进行多个“会话”的复杂性。为什么它应该更简单?实际上,如果您针对每个客户端使用一个定义良好的协议并使用一个连接,那么就更清晰易于维护。然而,这非常低效,因为他必须在一秒钟内发送许多消息并且每次都要建立一个TCP连接,可能由于有些时候在某些国家建立连接需要半秒钟,这意味着在某些情况下,他每秒只能发送两三条消息。 - JoulinRouge
显示剩余7条评论

6

但UDP广播应该可以解决这个问题

广播只在本地网络内起作用,相关的组播需要基础设施支持 - 这并不是微不足道的。

除此之外:对于"编写客户端/服务器应用程序"没有通用的规则。通信方法和协议高度依赖于使用情况,可以从简单的UDP或TCP数据包到具有可靠性和实时保证等复杂的消息传递体系。

因此,您需要做什么取决于您想要实现的内容,而您没有详细说明。


广播仅在本地网络内工作,因此服务器永远不会向客户端广播。我仍将使用UDP(为了速度)向每个客户端单独发送更新。那么对于从客户端发送到服务器的数据包该怎么办?(这些数据包不应该丢失,对吧?) - user2651804
我想要实现的是简单的通信。客户端应该能够像任何普通的客户端/服务器应用程序一样向服务器请求数据。如果服务器未能响应用户输入,则用户不应再次请求,因为数据包可能已经被随意丢失。客户端应该假设存在网络问题并再次向服务器请求(从单个用户输入发出多个请求)。就像我使用TCP时的情况一样。但是对于更新,我需要UDP,对吗? - user2651804
@user2651804:只是为了纠正你的一些假设:UDP和TCP只是传输方法,你可以在任何方向上使用它们(即使用TCP进行更新是可能的)。UDP不一定更快,但延迟可以更低,代价是可靠性。我建议看看现有的客户端/服务器场景,比如只使用TCP的HTTP。 - Steffen Ullrich
这个问题可能听起来很蠢,但我该如何“看”它呢?我认为你说得对,我正在做出许多错误的假设...但我不知道在哪里寻找更准确的信息。但是HTTP是一次性传输文件,对吗?它对UDP没有用处。你能否纠正我的假设,即大多数服务器/客户端应用程序将需要TCP(或其他一些方式来保证消息被传递),而且同时,但矛盾的是,其中一些将受益于自包含消息(UDP),因此也会使用它们? - user2651804
@user2651804:HTTP是一种请求/响应协议,可以在单个TCP连接中具有多个请求/响应。SMTP(邮件)、FTP、IMAP等都仅使用TCP。UDP主要由实时应用程序如RTP、RTSP(实时音频/视频)等使用,仅因为这些情况下需要短延迟并接受数据包丢失。在某些情况下,根据确切的用例,可以同时使用UDP和TCP(DNS、SIP)。同样,这完全取决于用例,大多数客户端/服务器应用程序使用TCP,因为大多数用例包括可靠性要求。 - Steffen Ullrich
1
@user2651804:我建议你首先独立于选择UDP或TCP协议的问题,设计应用程序的初始框架,只考虑通信模型。然后你可以根据每种协议的优缺点,决定哪些通信使用UDP,哪些使用TCP。 - Steffen Ullrich

1
忘记广播协议吧,它们根本不起作用,正如某人已经写过的那样。你需要将消息传递给每个客户端,创建一个类似于StateChangeDispatcher的东西。
UDP不可靠,但比TCP更快,特别是考虑到没有握手。握手不仅慢,而且会消耗一些带宽,特别是当你必须与许多客户端建立连接时。
同时使用UDP和TCP并不那么糟糕,很多著名的程序都采用了这种方法。例如,BitTorrent可以使用两种协议:UDP用于与跟踪器通信和管理DHT,TCP用于传输文件块。这就是他们选择的解释:跟踪器或DHT被定期查询,这意味着在一天的使用中,您可能会更新您的跟踪器数百次,如果有时候失败了也没关系,因为下一次您还是会更新您的跟踪器(DNS协议中的思想是一样的,您通过UDP发送请求,如果在合理的时间内没有收到响应,只需重新发送请求即可)。而TCP协议必须用于文件传输,因为您更喜欢更慢但绝对可靠。
另一个使用这两种协议的著名应用是Skype,但原因不同。视频通话采用UDP,因为TCP协议在接收到错误数据包时会停止并请求重新发送数据包,这需要时间。因此,最好不要停止通话,而是接收和处理错误数据包。这就是为什么在Skype中有时会收到糟糕的视频或难以理解的音频,你可以按原样接收视频,但速度太慢。TCP用于其他事情,如发送文件。
因此,你必须每秒向许多客户端发送许多消息。我不会为每个消息建立新的TCP连接。在我看来,你有以下两个选择:
1. 如果你能承受一些数据丢失,则通过UDP发送每个服务器状态更改。 2. 为每个客户端建立一个TCP连接,并使用该连接发送所有服务器状态更改,然后在客户端断开连接时关闭连接。
一个使用两个TCP连接的著名协议是FTP,您有一个连接用于向服务器发送指令和接收响应,另一个连接用于发送和接收文件。请注意,即使命令可以在时间上间隔很长(肯定比每秒钟多次),也不必为每个命令建立连接,以避免过多的握手。 您可以遵循许多模式,但请注意测试协议是昂贵的(需要很多时间,并且您可能需要超过10台计算机来查看是否一切正常),因此在开始编码之前请三思而后行,在互联网上,您可以找到一些数学方法来计算协议性能和带宽使用情况,但它们并不是非常简单。祝你好运。

你提到了Skype。TCP用于发送文件,难道也不会用于发送消息吗?或者用于“开始”通话,或者通过用户输入向服务器询问其他内容?这让我感到困惑。虽然有些应用程序显然可以从UDP中受益,但是有哪些应用程序可能没有TCP呢?你说同时使用两个协议“并不那么糟糕”。这很可能意味着我不应该这样做。我从UDP中受益。如果客户端没有更新,它们下次肯定会更新 - 就像你所说的那样。但是显然需要TCP。当客户端尝试更新服务器状态时。 - user2651804
其实,你能否提到一个仅使用UDP的应用程序,这样我们就可以从那里缩小问题范围。 - user2651804
我已经提到过,DNS协议仅使用UDP,并且它是应用层的协议,但我知道这是一个非常特殊的情况。当然,如果您不能承受数据丢失,您必须使用TCP,我的观点是同时使用两种协议并不是一种不好的做法(而且也不更加困难或昂贵)。您只需要确定在哪里可以允许数据丢失。user2651804在上面的回答中发表了一个有趣的评论,我完全同意。 - JoulinRouge
如果我不能承受数据丢失,我必须使用TCP。好吧,您是说DNS协议中存在数据丢失吗?为避免误解,我将解释一下我的DNS协议理解:我要求我的浏览器定位Web服务器。仅此而已。所以您说我使用UDP发出请求。但我主张,如果我有互联网连接,并且Web服务器在线并且能够处理请求-我将获得响应。这就是我在问题中称之为“可靠通信”的内容。用户输入不会丢失。因此,在我看来,DNS协议使用UDP来实现TCP的功能(重新发送数据包)。 - user2651804
正如这个线程中的每一个答案所说:https://dev59.com/UnDXa4cB1Zd3GeqP8yFi请使用TCP,而不是UDP。 - user2651804

0

你所询问的原理与DNS服务器中使用的相同。DNS通常使用UDP进行快速传输(UDP协议的原则),但不可靠。TCP较慢(带有ACK和有效载荷),但是可靠。因此,如我下面所述,使用两者,通过有效载荷区分。

DNS BIND文档:

通常,普通查询使用UDP,区域传输使用TCP。

但是,DNS将UDP查询和响应限制为约500个字节。如果响应大于该值,则服务器会发送高达500个字节并设置“截断”标志。然后,客户端应使用TCP执行相同的查询,这几乎没有限制响应大小...

如果您想同时更新所有服务器,请将它们注册到多播中,并通过一个进程与所有客户端服务器通信。这种方法虽然不太可靠,但速度很快。 https://en.wikipedia.org/wiki/IP_multicast 因此,您需要创建一个服务器,将多播伙伴注册并从中监听。为确保检测到UDP数据报在路由上丢失,每个UDP数据报都有其ID号。还要确保可以按不同的顺序接收数据报,因此应使用缓冲区或队列来计算正确的顺序。
希望这能帮到您。

0
如果是像动作游戏这样的客户端/服务器游戏,我建议您使用TCP来处理用户界面,因为您不希望丢失用户数据,但在游戏中,您不能承受TCP通信在快节奏多人游戏中的延迟。
因为在动作游戏中失去“一些”信息并不是那么糟糕,所以我建议在游戏中或任何需要非常快速通信的地方使用UDP。
但如果不是需要快速通信的游戏,我建议您使用TCP。

1
我同意,在动作游戏中使用UDP可能很有用。而且用户输入可能不会丢失。因此,我可以直接使用两种协议。这对我来说显而易见。但是一般情况下人们不鼓励这样做。 - user2651804

0

我感觉你正在考虑一种可靠的客户/服务器通信方法,这将类似于广播,避免了TCP/IP通信的开销。

我还可以建议您使用消息传递。创建一个消息驱动的bean(或消息客户端、消费者),该bean将被注册到消息通道或消息队列,并在PUSH方法上激活。

您的消息生产者创建一个消息,所有其他已注册的客户端服务器(消费者)都将被通知并异步处理它。 您可以使用任何平台,也可以使用开源的apache MQ。 https://en.wikipedia.org/wiki/Message_queue


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