多客户,在C#中的最佳实践异步插座? [英] Multi-client, async sockets in c#, best practices?

查看:117
本文介绍了多客户,在C#中的最佳实践异步插座?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图获得在C#中更好地理解TCP / IP套接字的,因为我想挑战自己,看看我是否能创建一个工作MMO基础结构(游戏世界,地图,玩家,等)纯粹是为了教育目的的我没有被那些另一个意图OMGZ IZ要让我r0x0r MMORPG,这将是比魔兽好!!!,你知道说什么IM。



无论如何,我想知道是否有人能提供一些线索作为一个如何可能接近设计这种系统以及需要什么样的东西,我应该注意的?



我最初的想法是将系统分解为执行特定任务与每个连接(它自己的端口上)独立客户端/服务器连接,如更新玩家/怪物阵地,发送和接收聊天消息等,这些我将使处理数据更容易,因为你不会总是需要把一个头的数据就知道数据包包含哪些信息。



这是否有意义,是有用的还是我刚才一路过来复杂的东西呢?



你的反应是非常大加赞赏。


解决方案

如果你正在做的套接字级别编程,那么无论你有多少个端口,打开每个消息类型,仍然需要有某种形式的报头。即使它是消息的其余部分的只是长度。话虽如此,很容易简单的头部和尾部结构添加到一个消息。我觉得它更容易只需要处理与客户端上的一个端口。



我相信现代的MMORPG游戏(甚至旧的)有两个级别的服务器。登录服务器验证了您作为付费客户。一经查实,这些传递你休要包含所有游戏世界信息的游戏服务器。即便如此,这仍然只需要在客户端有一个套接字开放,但并没有禁止拥有更多。



此外最MMORPGS也加密所有的数据。如果你正在写它作为一个练习的乐趣,那么这将不那么重要了。



对于一般的设计/写协议,这里的事情,我担心的:< BR>



字节序



在客户端和服务器始终保证有同样的字节顺序。如果不是我需要处理,在我的序列化代码。有多种方法来处理字节序




  1. 忽略它 - 显然,一个坏的选择

  2. 指定的该协议的字节顺序。这是上了年纪的协议做了这总是大端/做因此词网络秩序。实际上它并不无论哪个ENDIANESS仅指定您指定一个或另一个。一些硬皮旧网络程序员将得到拿起武器,如果你不使用大字节序,但如果您的服务器和大多数客户都是小端你真的不买比自己通过协议大端额外的工​​作,其他任何东西。

  3. 标记字节序中的每个标题 - 您可以添加一个cookie,这将告诉你客户机/服务器的字节序,让需要每封邮件进行相应的转换。 !额外的工作

  4. 请您与协议无关 - 如果您发送的一切作为ASCII字符串,那么字节顺序是无关紧要的,但也有很多更低效



  5. 4的我一般会选择2,并指定字节顺序是广大客户,现在天将小端的。



    向前和向后兼容



    请问该协议必须向前和向后兼容。答案几乎是可以肯定的。在这种情况下,这将决定我如何版本控制方面设计了所有的协议,以及如何创建各个消息处理细微的变化真的不应该是版本的一部分。你可以踢这个和使用XML,但你失去很多的效率。



    对于整个​​版本我通常设计简单的东西。客户端发送一个版本的消息指定它讲的版本X.Y,只要服务器可以支持该版本它发回了消息,确认该客户端的版本和一切向前进行。 。否则,未确认客户端和终止连接



    有关您有类似下面的每个消息:

      + ------------------------- + -------------- ----- + ----------------- + ------------------------ + $ b $ C |消息(4字节)的长度| MSGTYPE(2字节)|标志(4字节)|消息(长 -  6字节)| 
    + ------------------------- + ------------------- + ----------------- + ------------------------ +

    长度明显告诉你,消息是多久,不包括本身的长度。该MSGTYPE是消息的类型。这个只有两个字节,因为65356是大量的信息类型的应用程序。这些标志是否有让你知道什么是消息中连载。此字段的长度相结合,是什么给你的向前和向后兼容性

     常量uint32_t的FLAG_0 =(1 <<;< 0); 
    常量uint32_t的FLAG_1 =(1 <<;&。1);
    常量uint32_t的FLAG_2 =(1 <<; 2);
    ...
    常量uint32_t的RESERVED_32 =(1 <<;&下; 31);



    那么你的反序列化的代码可以执行类似如下:

      UINT32长度= MessageBuffer.ReadUint32(); 
    UINT32开始= MessageBuffer.CurrentOffset();
    UINT16 MSGTYPE = MessageBuffer.ReadUint16();
    UINT32标志= MessageBuffer.ReadUint32();

    如果(旗&安培; FLAG_0)
    {
    //读出任何FLAG_0代表。
    //单个或多个字段
    }
    // ...
    //读取其它标志
    // ...

    MessageBuffer.AdvanceToOffset(启动+长度);

    这可以让你的添加新领域以结束消息,而不必修改整个协议。它还确保旧的服务器和客户端将忽略他们不知道的标志。如果他们必须使用新的标志和字段,那么你只需要改变整体协议版本。



    使用框架工作或没有



    有我会考虑使用的业务应用程序的各种网络框架。除非我有特别需要划伤我会去与一个标准的框架。你的情况,你要学习套接字级别编程,所以这已经回答了你的问题。



    如果一个人确实使用了一个框架,确保它解决了上述两个问题,或者至少,如果你需要定制它在这些领域并不妨碍你。



    我是不是处理一个第三方



    在很多情况下,你可能会处理与第三方服务器/客户端需要与沟通。这意味着有几个可能性:




    • 他们已经有了一种协议定义 - 只需使用他们的协议

    • 您已经有一个协议定义(和他们愿意使用它) - 再简单的使用定义的协议

    • 他们用一个标准框架(基于WSDL等) - 使用框架。

    • 任何一方都定义的协议 - 尝试determing基于所有因素在手边的最佳解决方案(所有我这里提到的那些),以及它们的competencey的水平(至少至于你可以告诉)。无论确保双方同意并理解的协议。从经验,这可以是痛苦或令人愉快。这取决于你是谁一起工作。



    在你会不会与第三方合作无论是哪种情况,所以这真的只是添加的完整性。



    我觉得我可以写更多关于这一点,但它已经是相当长的。我希望这可以帮助,如果您有任何具体问题,只是问#2



    一个编辑回答knoopx的问题:




    I am trying to gain a better understanding of tcp/ip sockets in c#, as i want to challenge myself to see if i can create a working MMO infrastructure (game world, map, players, etc) purely for educational purposes as i have no intention of being another one of those "OMGZ iz gonna make my r0x0r MMORPG that will be better than WoW!!!", you know what im talking about.

    Anyway, i was wondering if anyone can shed some light as to how one might approach designing this kind of system and what kinds of things are required, and what i should watch out for?

    My initial idea was to break up the system into seperate client/server connections with each connection (on its own port) performing a specific task, such as updating player/monster positions, sending and receiving chat messages, etc. which to me would make processing the data easier because you wouldn't always need to put a header on the data to know what information the packet contains.

    Does that make sense and is useful or am i just way over complicating things?

    your responses are very much appreciated.

    解决方案

    If you are doing socket level programming, then regardless of how many ports you open for each message type, you still need to have a header of some sort. Even if it is just the length of the rest of the message. Having said that it is easy to add a simple header and tail structure to a message. I would think it is easier to only have to deal with one port on the client side.

    I believe the modern MMORPGs (and maybe even the old ones) had two levels of servers. The login servers which verify you as a paying client. Once verified these pass you off to the game server which contain all the game world information. Even so this still only requires the client to have one socket open, but does not disallow having more.

    Additionally most MMORPGS also encrypt all their data. If you are writing this as an exercise for fun, then this will not matter so much.

    For designing/writing protocols in general here are the things I worry about:

    Endianess

    Are the client and server always guaranteed to have the same endianess. If not I need to handle that in my serialization code. There are multiple ways to handle endianess.

    1. Ignore it - Obviously a bad choice
    2. Specify the endianness of the protocol. This is what the older protocols did/do hence the term network order which was always big endian. It doesn't actually matter which endianess you specify just that you specify one or the other. Some crusty old network programmers will get up in arms if you don't use big endianess, but if your servers and most clients are little endian you really aren't buying yourself anything other than extra work by making the protocol big endian.
    3. Mark the Endianess in each header - You can add a cookie which will tell you the endianess of the client/server and let each message be converted accordingly as needed. Extra work!
    4. Make your protocol agnostic - If you send everything as ASCII strings, then endianess is irrelevant, but also a lot more inefficient.

    Of the 4 I would usually choose 2, and specify the endianess to be that of the majority of the clients, which now days will be little endian.

    Forwards and Backwards Compatibility

    Does the protocol need to be forwards and backwards compatible. The answer should almost always be yes. In which case this will determine how I design the over all protocol in terms of versioning, and how each individual message is created to handle minor changes that really shouldn't be part of the versioning. You can punt on this and use XML, but you lose a lot of efficiency.

    For the overall versioning I usually design something simple. The client sends a versioning message specifying that it speaks version X.Y, as long as the server can support that version it sends back a message acknowledging the version of the client and everything proceeds forward. Otherwise it nacks the client and terminates the connection.

    For each message you have something like the following:

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

    The length obviously tells you how long the message is, not including the length itself. The MsgType is the type of the message. Only two bytes for this, since 65356 is plenty of messages types for applications. The flags are there to let you know what is serialized in the message. This field combined with the length is what gives you your forwards and backwards compatibility.

    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);
    

    Then your deserialization code can do something like the following:

    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);
    

    This allows you to add new fields to the end of the messages without having to revision the entire protocol. It also ensures that old servers and clients will ignore flags they don't know about. If they have to use the new flags and fields, then you simply change the overall protocol version.

    Use a Frame Work or Not

    There are various network frameworks I would consider using for a business application. Unless I had a specific need to scratch I would go with a standard framework. In your case you want to learn socket level programming, so this is a question already answered for you.

    If one does use a framework make sure it addresses the two concerns above, or at least does not get in your way if you need to customize it in those areas.

    Am I dealing with a third party

    In many cases you may be dealing with a third party server/client you need to communicate with. This implies a few scenarios:

    • They already have a protocol defined - Simply use their protocol.
    • You already have a protocol defined (and they are willing to use it) - again simple use the defined protocol
    • They use a standard Framework (WSDL based, etc) - Use the framework.
    • Neither party has a protocol defined - Try to determing the best solution based on all the factors at hand (all the ones I mentioned here), as well as their level of competencey (at least as far as you can tell). Regardless make sure both sides agree on and understand the protocol. From experience this can be painful or pleasant. It depends on who you are working with.

    In either case you will not be working with a third party, so this is really just added for completeness.

    I feel as if I could write much more about this, but it is already rather lengthy. I hopes this helps and if you have any specific questions just ask on Stackoverflow.

    An edit to answer knoopx's question:

    这篇关于多客户,在C#中的最佳实践异步插座?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆