我试图更好地理解c#中的tcp / ip套接字,因为我想挑战自己,看看我是否可以创建一个可用于教育目的的有效MMO基础设施(游戏世界,地图,玩家等)我无意成为另一个“OMGZ iz让我的r0x0 MMORPG比魔兽更好!!!”,你知道我在说什么。

无论如何,我想知道是否有人可以阐明一个人如何设计这种系统以及需要什么样的东西,以及我应该注意什么?

我最初的想法是将系统分解为单独的客户端/服务器连接,每个连接(在自己的端口上)执行特定任务,例如更新玩家/怪物位置,发送和接收聊天消息等等。我会更容易处理数据,因为你不总是需要在数据上放一个标题来知道数据包包含哪些信息。

这是否有意义且有用,还是我只是简单地使事情复杂化?

非常感谢您的回复。

有帮助吗?

解决方案

如果您正在进行套接字级编程,那么无论您为每种消息类型打开多少端口,您仍需要具有某种类型的标头。即使它只是消息其余部分的长度。已经说过很容易为消息添加简单的头和尾结构。我认为只需处理客户端的一个端口就更容易了。

我相信现代MMORPG(甚至可能是旧版)有两级服务器。登录服务器验证您是付费客户端。一旦验证,这些将您带到包含所有游戏世界信息的游戏服务器。即便如此,这仍然只需要客户端打开一个套接字,但不允许有更多。

此外,大多数MMORPGS还会加密所有数据。如果你把它写成一个有趣的练习,那么这并不重要。

对于一般设计/编写协议,我担心的是:

<强>字节序

客户端和服务器是否始终保证具有相同的endianess。如果不是,我需要在序列化代码中处理它。有多种方法可以处理结束。

  1. 忽略它 - 显然是一个糟糕的选择
  2. 指定协议的字节顺序。这就是旧协议做/做的事情,因此称为网络顺序这个术语总是大端的。实际上,只要您指定一个或另一个,您指定哪个字节顺序并不重要。如果你不使用大的endianess,一些硬的老网络程序员会站起来,但如果你的服务器和大多数客户端都是小端,你真的不会通过制定协议大端来购买除了额外工作以外的任何东西。
  3. 在每个标题中标记Endianess - 您可以添加一个cookie,它将告诉您客户端/服务器的endianess,并根据需要相应地转换每条消息。额外的工作!
  4. 使您的协议无关 - 如果您将所有内容作为ASCII字符串发送,那么endianess就无关紧要了,但也效率低得多。
  5. 在4中我通常会选择2,并指定endianess为大多数客户端,现在几天将是小端。

    前向和后向兼容性

    协议是否需要向前兼容。答案应该几乎总是肯定的。在这种情况下,这将决定我如何在版本控制方面设计整个协议,以及如何创建每个单独的消息来处理实际上不应该成为版本控制的一部分的微小更改。你可以利用这个并使用XML,但是你会失去很多效率。

    对于整体版本控制,我通常会设计一些简单的东西。客户端发送一个版本控制消息,指出它说的是版本X.Y,只要服务器可以支持该版本,它就会发回一条确认客户端版本的消息,一切都在前进。否则它会解密客户端并终止连接。

    对于每条消息,您都有以下内容:

    +-------------------------+-------------------+-----------------+------------------------+
    | Length of Msg (4 bytes) | MsgType (2 bytes) | Flags (4 bytes) | Msg (length - 6 bytes) |
    +-------------------------+-------------------+-----------------+------------------------+
    

    长度显然告诉你消息有多长,不包括长度本身。 MsgType是消息的类型。这只有两个字节,因为65356是应用程序的大量消息类型。标志是为了让您知道消息中序列化的内容。此字段与长度相结合,可以为您提供向前和向后兼容性。

    const uint32_t FLAG_0  = (1 << 0);
    const uint32_t FLAG_1  = (1 << 1);
    const uint32_t FLAG_2  = (1 << 2);
    ...
    const uint32_t RESERVED_32 = (1 << 31);
    

    然后您的反序列化代码可以执行以下操作:

    uint32 length = MessageBuffer.ReadUint32();
    uint32 start = MessageBuffer.CurrentOffset();
    uint16 msgType = MessageBuffer.ReadUint16();
    uint32 flags = MessageBuffer.ReadUint32();
    
    if (flags & FLAG_0)
    {
        // Read out whatever FLAG_0 represents.
        // Single or multiple fields
    }
    // ...
    // read out the other flags
    // ...
    
    MessageBuffer.AdvanceToOffset(start + length);
    

    这样,您就可以添加新字段到邮件的结尾,而无需修改整个协议。它还确保旧服务器和客户端将忽略他们不知道的标志。如果他们必须使用新的标志和字段,那么您只需更改整个协议版本。

    <

其他提示

我认为你需要在走路前和跑步前爬行。首先得到框架和连接的设计,然后担心可扩展性。如果您的目标是学习C#和tcp / ip编程,那么不要让自己更难。

继续您的初步想法并将数据流分开。

玩得开心,祝你好运

我会建议针对不同信息的多个连接。我会设计一些协议,其中包含一个包含要处理的信息的标头。尽可能少地使用资源。此外,您可能不希望从客户端向服务器发送各种位置和统计信息的更新。否则,您可能最终会遇到用户可以修改发送回服务器的数据的情况。

假设用户伪造怪物的位置以通过某些东西。我只会更新用户的位置向量和动作。让其余的信息由服务器处理和验证。

是的,我认为你对单一连接是正确的,当然客户端不会向服务器发送任何实际数据,更像是“向前移动”,“向左转”等简单命令,以及服务器将移动地图上的字符并将新坐标发送回客户端。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top