【问题标题】:What is the most appropriate way to write a multiplayer server?编写多人服务器的最合适方法是什么?
【发布时间】:2015-04-02 00:08:25
【问题描述】:

我目前正在为一个快节奏的多人游戏编写服务器,它应该在 UDP 中运行 (我读到 TCP 不合适,因为它处理丢包的方式,在需要及时传递数据的应用程序中,请纠正我如果 TCP 更有用) 该游戏不是大型多人游戏,应该主要由玩家自己托管,可能在本地专用服务器上。虽然我很确定客户端需要在 NIO 中,以避免网络问题导致游戏延迟,但我还不知道如何编写服务器。以下是我考虑过的可能性列表:

  • 为每个播放器使用单独的线程,每个播放器都绑定一个套接字
  • 使用单个套接字循环遍历每个播放器
    • 做同样的事情,但使用 NIO
  • 使用一个套接字同时向所有客户端广播消息
  • 使用一个套接字接收,一个在单独的线程上发送

在处理客户数量相对较少的时间关键型数据时,哪一种方法最合适?(不超过 16 个)

【问题讨论】:

  • 有多少客户是“低数量”?
  • @Malt 我现在工作的前提是 16 岁以上。
  • 我可以问一下那个否决票吗?我已尝试投入尽可能多的研究工作,但我看不出哪里失败了。
  • 您对 TCP 处理丢包的方式到底有什么问题? NB 多播与广播不同,多播套接字用于接收多播:您不需要多播套接字来发送。
  • @EJP 在丢弃的数据包到达之前,不会收到更多数据包,如果数据在几毫秒内过时,这会适得其反。假设一个带有船位的理论数据包被丢弃,你最好收到下一个并继续前进,而不是等待旧的。

标签: java multithreading sockets udp server


【解决方案1】:

对于 16 个客户端,除非您的硬件非常糟糕,或者您进行了一些令人发指的处理,否则无论采用哪种方式,您的性能都会很好。这意味着您可以以任何方式编写它,以使您更容易编写和维护。

关于您的选择的几句话:

为每个播放器使用单独的线程,每个播放器绑定一个套接字

这种方法对于较少数量的 TCP 套接字更有意义。如果你使用 UDP,你只有一个套接字,所以没有必要。但是,如果您的游戏需要每个客户端进行一些并行处理,那么每个客户端都有一个线程很有用。

使用单个套接字循环遍历每个玩家

这可能是最简单的选择,除非您的游戏需要非常特殊。正如我之前所说,你应该在性能方面做得很好。因此,如果它使编写代码更容易做到这一点。避免premature optimizations

使用一个套接字接收,一个单独的发送 线程

除非你有充分的理由这样做,否则我会避免这个想法。这可能(不必要地)在网络方面使事情复杂化,因为客户端会将数据包发送到端口 X,并从端口 Y 获得回复。NAT 将是一个严重的问题。

使用一个套接字同时向所有客户端广播消息

首先,这只适用于一个方向 - 从服务器到客户端。其次,这只适用于inside a LAN,因为路由器不转发广播数据包。如果您对这两个限制都满意,并且发现自己正在设计一个系统,在该系统中服务器将一些数据包发送给所有玩家(至少发送给同一网络中的玩家),那可能不是一个坏主意。就性能而言,除非您发送大量数据包,否则您可能不会注意到向每个玩家发送单独副本和发送广播之间的区别。

【讨论】:

  • 谢谢。硬件烂在意料之中,因为服务器将由玩家托管,这就是我担心的原因。您对广播数据包有什么建议吗?
  • @Kravaros 我也添加了一些关于广播的内容。关于硬件——再说一次,我不知道你在设计什么游戏,但我敢打赌,现代手机可以轻松地充当 16 名玩家的服务器,只要它不做任何过于密集的事情。如果我是你,我会从一个简单的实现开始,看看它是否有效。如果没有,那么(并且只有这样)找到瓶颈并使用上述方法修复它们。
【解决方案2】:

TCP 有更多开销,导致传输相同信息所需的带宽更高。然而,如果 TCP 没有到达,TCP 确实具有被重新发送的优势。如果您使用 UDP,请确保您通过网络发送的消息会丢失。例如,如果您将动作作为添加和更改发送,那么它将很快与客户端失去同步。传输时需要覆盖值。

每个玩家都需要一个线程来监听他们的输出,例如击键或动作,因为您必须等待每个线程上的数据,因此它会阻塞玩家 1,直到他们做出动作而玩家 2 不会在玩家 1 之前能够采取行动。

广播游戏状态可能是有道理的 - 但我没有这方面的经验,也没有像快节奏多人游戏那样时间紧迫的东西

【讨论】:

  • 我相信在 UDP 中,与 TCP 不同,您可以在单个线程&&socket 中接收所有通信,但更有知识的人可能会证实这一点,
  • @Kravaros 是正确的。与 UDP 不同,TCP 套接字是两台机器不同端点之间的流。
  • 好的,看起来确实是这样:docs.oracle.com/javase/tutorial/networking/datagrams/… 虽然你需要在每一端都有一个线程来分别发送和接收,因为接收仍然是一个阻塞方法调用
  • Multiplexed NIO 使此答案的第二段不正确,并且您的评论紧接在此之上。您可以在一个线程中完成所有操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-27
  • 2015-01-20
  • 2021-05-26
相关资源
最近更新 更多