我应该如何构建我的(主要是)基于文本的游戏服务器?

发布于 2024-11-26 16:04:55 字数 1316 浏览 2 评论 0原文

想想 MUD/MUCK,但也许带有头像或现场插图。我选择的语言是ruby

我需要处理多个持久连接,并在服务器与其各个客户端之间异步传输数据。单个数据库必须根据客户端会话中发生的活动保持最新。每个客户端会话中的活动可能需要立即更新多个其他客户端(用户进入房间;用户向另一个用户发送私人消息)。

这是一个目标项目一个学习项目,所以我的目的是重新发明一两个轮子来了解更多关于并发网络编程的知识。然而,我对并发和网络编程都很陌生;此前,我几乎专门从事 Web 应用程序中的非持久同步 HTTP 请求领域。因此,我想确保我正在重新发明正确的轮子。

根据 emboss 的出色回答,我已经开始研究某些 HTTP 服务器的内部结构,因为网络应用程序通常可以避免由于服务器本身抽象问题的彻底程度而导致的线程问题。

我不想使用 EventMachine 或 GS​​erver,因为我还不明白它们的作用。一旦我对它们如何工作、它们解决什么问题以及它们为什么有用有了一个总体了解,我就会感到很舒服。我的目标不是“编写游戏”,而是“编写游戏并了解一些较低级别的东西是如何工作的”。我也不清楚某些术语的界限;例如,“I/O 非绑定应用程序”是“事件驱动应用程序”的超集吗?反之亦然?

我当然对实现我的目标的一种正确方法感兴趣(如果存在的话),但总的来说,我想了解为什么它是正确的方法以及为什么其他方法不太可取。

您可以建议的任何书籍、电子书、在线资源、示例项目或其他花絮都是我真正想要的。

我现在做事的方式是使用 IO#select 阻止连接的套接字列表,超时时间为 0.1 秒。它将读取的任何信息推送到线程安全的读取队列中,然后每当超时时,它就会从线程安全的写入队列中提取数据。我不确定超时是否应该更短。还有第二个线程轮询套接字处理线程的读取队列并处理“请求”。这比我最初的工作方式要好,但仍然可能不理想。

我在黑客新闻上发布了这个问题,并链接到我正在使用的一些资源;任何类似的东西都会很棒:

Think MUDs/MUCKs but maybe with avatars or locale illustrations. My language of choice is ruby.

I need to handle multiple persistent connections with data being asynchronously transferred between the server and its various clients. A single database must be kept up-to-date based on activity occurring in the client sessions. Activity in each client session may require multiple other clients to be immediately updated (a user enters a room; a user sends another user a private message).

This is a goal project and a learning project, so my intention is to re-invent a wheel or two to learn more about concurrent network programming. However, I am new to both concurrent and network programming; previously I have worked almost exclusively in the world of non-persistent, synchronous HTTP requests in web apps. So, I want to make sure that I'm reinventing the right wheels.

Per emboss's excellent answer, I have been starting to look at the internals of certain HTTP servers, since web apps can usually avoid threading concerns due to how thoroughly the issue is abstracted away by the servers themselves.

I do not want to use EventMachine or GServer because I don't yet understand what they do. Once I have a general sense of how they work, what problems they solve and why they're useful, I'll feel comfortable with it. My goal here is not "write a game", but "write a game and learn how some of the lower-level stuff works". I'm also unclear on the boundaries of certain terms; for example, is "I/O-unbound apps" a superset of "event-driven apps"? Vice-versa?

I am of course interested in the One Right Way to achieve my goal, if it exists, but overall I want to understand why it's the right way and why other ways are less preferable.

Any books, ebooks, online resources, sample projects or other tidbits you can suggest are what I'm really after.

The way I am doing things right now is by using IO#select to block on the list of connected sockets, with a timeout of 0.1 seconds. It pushes any information read into a thread-safe read queue, and then whenever it hits the timeout, it pulls data from a thread-safe write queue. I'm not sure if the timeout should be shorter. There is a second thread which polls the socket-handling thread's read queue and processes the "requests". This is better than how I had it working initially, but still might not be ideal.

I posted this question on Hacker News and got linked to a few resources that I'm working through; anything similar would be great:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绿光 2024-12-03 16:04:55

尽管您可能不喜欢听到它,但我仍然建议您首先开始研究 HTTP 服务器。尽管对你来说,为它们编程似乎很无聊、同步且非持久,但这只是因为服务器的创建者很好地向你隐藏了血淋淋的细节 - 如果你仔细想想,Web 服务器是如此<强>不同步(这并不是说数百万人必须等待阅读这篇文章直到你完成......并发:)......而且因为这些野兽做得很好(是的,我知道我们对他们大喊大叫,但归根结底,大多数 HTTP 服务器都是出色的软件)如果您想了解高效的多线程,那么这是一个明确的起点。操作系统和编程语言或游戏的实现是另一个很好的来源,但可能离您想要实现的目标有点远。

如果你真的想亲自动手,我建议你先把自己定位在 WEBrick 之类的地方 -它随 Ruby 一起提供,并且完全用 Ruby 实现,因此您将在那里了解有关 Ruby 线程概念的所有内容。但请注意,您永远无法接近 Rack 解决方案的性能,该解决方案位于用 C 实现的 Web 服务器之上,例如

因此,如果您真的想认真一点,那么如果您打算支持 HTTP,则必须在 C(++) 中推出自己的服务器实现,并且可能使其支持 Rack。我想说,这是一项艰巨的任务,特别是如果您希望最终结果具有竞争力的话。 C 代码可以非常快,但也很容易变得非常慢,这是低级代码的本质。我们还没有讨论内存管理和安全性。但如果这确实是您的愿望,那就去做吧,但我会首先深入研究众所周知的服务器实现来获取灵感。了解它们如何使用线程(池)以及它们如何实现“会话”(您想要持久性)。您想要的所有事情都可以使用 HTTP 完成,如果与巧妙的 REST 接口一起使用,效果会更好,支持您提到的所有功能的现有应用程序就是活生生的证明。所以朝这个方向走并不是完全错误的。

如果您仍然想发明自己的专有协议,请将其基于 TCP/IP 作为可接受的最低公分母。如果超出这个范围,您的子孙可能仍在编写一个项目。当涉及到网络编程时,这确实是我敢于达到的最低水平。

无论您是否将其用作库,请研究 EventMachine 及其概念模型。在学习/重新发明正确的轮子的背景下,在您的旅程中忽视事件驱动(“非阻塞”)IO 是一种疏忽。事件驱动编程的开胃菜,解释了 Node.js 作为 网络服务器

根据您的要求:异步通信,多个“订阅者”对集中发布的“事件”做出反应;嗯,这听起来确实是事件驱动/基于消息架构的一个很好的候选者。


一些可能对您的旅程有帮助的书籍(仅限 Linux/C,但概念是通用的):

(那些是经典)

  • Linux 编程界面 - 如果您只想购买一本书,那就买这本书吧,我还没有完全读完,但它确实令人惊叹,涵盖了

您可能想要的冒险项目 所需了解的所有主题查看:

Although you probably don't like to hear it I would still recommend to start investigating HTTP servers first. Although programming for them seemed boring, synchronous, and non-persistent to you, that's only because the creators of the servers did their job to hide the gory details from you so tremendously well - if you think about it, a web server is so not synchronous (it's not that millions of people have to wait for reading this post until you are done... concurrency :) ... and because these beasts do their job so well (yeah, I know we yell at them a lot, but at the end of the day most HTTP servers are outstanding pieces of software) this is the definite starting point to look into if you want to learn about efficient multi-threading. Operating systems and implementations of programming languages or games are another good source, but maybe a bit further away from what you intend to achieve.

If you really intend to get your fingers dirty I would suggest to orient yourself at something like WEBrick first - it ships with Ruby and is entirely implemented in Ruby, so you will learn all about Ruby threading concepts there. But be warned, you'll never get close to the performance of a Rack solution that sits on top of a web server that's implemented in C such as thin.

So if you really want to be serious, you would have to roll your own server implementation in C(++) and probably make it support Rack, if you intend to support HTTP. Quite a task I would say, especially if you want your end result to be competitive. C code can be blazingly fast, but it's all to easy to be blazingly slow as well, it lies in the nature of low-level stuff. And we haven't discussed memory management and security yet. But if it's really your desire, go for it, but I would first dig into well-known server implementations to get inspiration. See how they work with threads (pooling) and how they implement 'sessions' (you wanted persistence). All the things you desire can be done with HTTP, even better when used with a clever REST interface, existing applications that support all the features you mentioned are living proof for that. So going in that direction would be not entirely wrong.

If you still want to invent your own proprietary protocol, base it on TCP/IP as the lowest acceptable common denominator. Going beyond that would end up in a project that your grand-children would probably still be coding on. That's really as low as I would dare to go when it comes to network programming.

Whether you are using it as a library or not, look into EventMachine and its conceptual model. Overlooking event-driven ('non-blocking') IO in your journey would be negligent in the context of learning about/reinventing the right wheels. An appetizer for event-driven programming explaining the benefits of node.js as a web server.

Based on your requirements: asynchronous communication, multiple "subscribers" reacting to "events" that are centrally published; well that really sounds like a good candidate for an event-driven/message-based architecture.


Some books that may be helpful on your journey (Linux/C only, but the concepts are universal):

(Those were the classics)

  • The Linux programming interface - if you just intend to buy one book, let it be this one, I'm not entirely through yet but it is truly amazing and covers all the topics you need to know about for your adventure

Projects you may want to check out:

偏闹i 2024-12-03 16:04:55

我建议阅读一些有关独角兽网络服务器设计的内容。这应该能让您深入了解线程与进程的讨论。

http://unicorn.bogomips.org/DESIGN.html

http://tomayko.com/writings/unicorn-is-unix

I'd recommend reading a bit about unicorn web-server design. That should give you some insight into thread vs. process discussion.

http://unicorn.bogomips.org/DESIGN.html

http://tomayko.com/writings/unicorn-is-unix

浪菊怪哟 2024-12-03 16:04:55

我对 Ruby 了解不多 - 抱歉 - 但我认为架构需要由您的需求驱动(母性、苹果派......我知道)。

如果您正在构建需要扩展到大量用户的东西,您的架构需要反映这一点 - 如果您的运营规模较小,那么您最终会做出不一定需要做出的各种决策。

响应时间也起着重要作用 - 我认为这对于 MUD 风格的游戏来说并不是什么大问题,但对于网络服务器或 FPS 游戏来说,这是一个大问题。

话虽如此,我所知道的唯一与您所描述的系统类似的系统使用事件驱动的编程模型,客户端触发事件,系统更新其内部状态,并通知受影响的客户端。 “内部状态”实际上存储在单独的应用程序中,再次使用套接字进行通信 - 这允许通过添加更多服务器来处理客户端交互来扩展应用程序。不确定您需要那种复杂程度。

线程确实是一个真正的问题,它创建了难以测试的代码,因此,虽然 dash-tom-bang 的答案确实有点偏离主题,但可测试性一个重要的问题。

I don't know much about Ruby - sorry - but I think the architecture needs to be driven by your requirements (motherhood, apple pie...I know).

If you're building something that needs to scale to large numbers of users, your architecture needs to reflect that - and you end up making all sorts of decisions you don't necessarily need to make if you're operating at more modest scale.

Response time also plays a big part - I don't think that is such a big deal with MUD style games, but for web servers or FPS games, it's a huge issue.

Having said that - the only systems similar to what you describe that I know in any detail use an event driven programming model - clients trigger events, the system updates its internal state, and notifies affected clients. The "internal state" is actually stored in a separate application, again communicating using sockets - this allows the app to be scaled by adding more servers to handle client interaction. Not sure you need that level of complexity.

Threading is indeed a real issue, and it creates hard-to-test code, so whilst dash-tom-bang's answer is indeed a little off topic, testability is a significant concern.

み青杉依旧 2024-12-03 16:04:55

正确的方法是使用测试驱动开发。然后,您将在需要发明它的那一刻发现您需要重新发明什么。

从“连接”测试开始,并断言返回消息“你好,用户”。从那里开始一步一步地进行。

The right way is to use test driven development. You will then discover what you need to reinvent at the exact moment that you need to invent it.

Start with a test that "connects" and assert that the message "hello, user" is returned. Take it one step at a time from there.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文