是否存在用于编写通过套接字与n个客户端通信的基于回合的服务器的模式?


14

我正在使用一种通用游戏服务器,该服务器可以为任意数量的TCP套接字网络客户端在玩游戏时管理游戏。我有一个“设计”与管道胶带一起被破解,但它既脆弱又不灵活。对于如何编写健壮而灵活的客户端/服务器通信,是否存在一种完善的模式?(如果没有,您将如何改善下面的内容?)

大概我有这个:

  • 设置游戏时,服务器为每个玩家套接字提供一个线程,以处理来自客户端的同步请求和来自服务器的响应。
  • 然而,一旦游戏开始,除了一个睡眠之外,所有线程都将循环,并且该线程一次循环遍历所有玩家,以传达有关其行动的信息(反向请求响应)。

这是我目前所拥有的图表;单击以获取较大的版本/可读的版本,或单击66kB PDF

流程顺序图

问题:

  • 它要求玩家用正确的信息准确地依次做出回应。(我想我可以让每位球员随机回应,只有在他们给我有效举动后才继续前进。)
  • 除非轮到他们,否则不允许玩家与服务器交谈。(我可以让服务器向他们发送有关其他播放器的更新,但不处理异步请求。)

最终要求:

  • 性能不是最重要的。这将主要用于非实时游戏,并且主要用于使AI相互对抗,而不是抽动的人。

  • 游戏将始终基于回合制(即使分辨率很高)。每个玩家总是在其他所有玩家获得回合之前获得一招。

服务器服务器的实现恰好在Ruby中,如果有所不同的话。


如果您为每个客户端使用TCP套接字,那么此上限不限制为(65535-1024)客户端吗?
o0'。

1
Lohoris为什么会出现问题?大多数人将很难获得6000个并发用户,更不用说60000。–
Kylotan

@Kylotan确实。如果我有10个以上的并发AI,我会感到惊讶。:)
Phrogz

出于好奇,您使用了什么工具来创建该图?
matthias 2014年

@matthias不幸的是,这在Adobe Illustrator中是过多的自定义工作。
Phrogz 2014年

Answers:


10

我不确定您要实现的目标是什么。但是,游戏服务器中经常使用一种模式,它可能会对您有所帮助。使用消息队列。

更具体地说:客户端将消息发送到服务器时,请勿立即对其进行处理。而是解析它们并将其放入此特定客户端的队列中。然后,在某个主循环中(甚至可能在另一个线程上)依次遍历所有客户端,从队列中检索消息并进行处理。当处理表明该客户的转机已结束时,请继续进行下一个。

这样,客户就不必严格按部就班地工作。只有足够快的速度,这样在客户端得到处理时,您就可以在队列中排队(当然,您可以等待客户端,或者在出现延迟时跳过客户端)。您还可以通过添加“异步”队列来添加对异步请求的支持:当客户端发送特殊请求时,该请求会添加到该特殊队列中;与客户的队列相比,此队列的检查和处理频率更高。


1

硬件线程的伸缩性不够好,无法使“每位玩家一个”成为3位玩家的合理想法,而知道何时唤醒它们的逻辑将变得越来越复杂。一个更好的主意是找到一个用于Ruby的异步I / O包,该包将允许您在进行读取或写入操作时不必暂停整个程序线程即可发送和接收数据。这也解决了等待玩家响应的问题,因为您的读取操作不会挂任何线程。相反,您的服务器可以只检查时间限制是否已到期,然后相应地通知其他玩家。

基本上,“异步I / O”是您要查找的“模式”,尽管它并不是真正的模式,但更是一种方法。无需在套接字上显式调用“读取”并暂停程序直到数据到达,而是将系统设置为在数据准备就绪时调用“ onRead”处理程序,然后继续进行处理。

每个插座都有一个转弯

每个玩家都有一个回合,每个玩家都有一个发送数据的套接字,这有点不同。有一天,您可能不希望每个玩家一个插座。您可能根本不使用套接字。将责任区分开。抱歉,这听起来像是一个不重要的细节,但是当您将设计中的概念融合在一起时,应该有所不同,那么这将使寻找和讨论更好的方法变得更加困难。


1

当然,有多种方法可以实现,但是就我个人而言,我将完全跳过单独的线程,而仅使用事件循环。执行此操作的方式在某种程度上取决于您所使用的I / O库,但是基本上,主服务器循环如下所示:

  1. 设置连接池和用于新连接的侦听套接字。
  2. 等待一些事情发生。
  3. 如果某物是新连接,请将其添加到池中。
  4. 如果这是客户的要求,请检查是否可以立即处理。如果是,请执行;如果不是,则将其放入队列,然后(可选)将确认发送给客户端。
  5. 同时检查队列中是否有您现在可以处理的东西;如果是这样,请执行此操作。
  6. 返回步骤2。

例如,假设您有n个客户参与游戏。然后当他们中的第n-1个人发送他们的动作时,您只需检查该动作看起来是否有效,然后发回一条消息,说已收到该动作,但您仍在等待其他玩家移动。在所有n位玩家都已移动之后,您将处理所有已保存的移动并将结果发送给所有玩家。

您还可以优化此方案以包括超时-大多数I / O库应该具有某种机制,可以等待新数据到达经过给定的时间量。

当然,您也可以为每个连接使用单独的线程来实现这样的事情,方法是让那些线程传递它们无法直接处理到运行一个循环的中央线程(每个游戏一个或每个服务器一个)的任何请求。如上所示,除了它与连接处理程序线程对话而不是直接与客户端对话。是否要找到比单线程方法更简单或更复杂的方法,完全取决于您。

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.