多客户端,c# 中的异步套接字,最佳实践? [英] Multi-client, async sockets in c#, best practices?

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

问题描述

我正在尝试更好地了解 c# 中的 tcp/ip 套接字,因为我想挑战自己,看看我是否可以创建一个纯粹用于教育目的的 MMO 基础设施(游戏世界、地图、玩家等)作为我无意成为OMGZ iz 会让我的 r0x0r MMORPG 变得比 WoW 更好!!!"的另一个人,你知道我在说什么.

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?

非常感谢您的回复.

推荐答案

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

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.

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

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.

此外,大多数 MMORPGS 还会加密其所有数据.如果你写这个是为了好玩,那么这就不重要了.

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:

字节序

客户端和服务器是否总是保证具有相同的字节序.如果不是,我需要在我的序列化代码中处理它.有多种方法可以处理字节序.

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. 忽略它 - 显然是个糟糕的选择
  2. 指定协议的字节序.这是旧协议所做的/所做的,因此术语网络顺序始终是大端.您指定哪种字节序实际上并不重要,只需指定一个或另一个即可.如果你不使用大字节序,一些顽固的老网络程序员会站起来,但如果你的服务器和大多数客户端都是小字节序的,你真的不会为自己买任何东西,除了通过使协议大字节序做额外的工作.
  3. 在每个标头中标记字节序 - 您可以添加一个 cookie,它会告诉您客户端/服务器的字节序,并根据需要相应地转换每条消息.额外的工作!
  4. 使您的协议不可知 - 如果您将所有内容都作为 ASCII 字符串发送,那么字节顺序无关紧要,但效率也会低得多.

在这 4 个中,我通常会选择 2 个,并将字节序指定为大多数客户端的字节序,现在这些客户端将是小字节序.

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.

向前和向后兼容性

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

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.

对于整体版本控制,我通常会设计一些简单的东西.客户端发送一个版本控制消息,指定它使用 X.Y 版本,只要服务器可以支持该版本,它就会发回一条消息确认客户端的版本,然后一切继续进行.否则它会拒绝客户端并终止连接.

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

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

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.

是否使用框架

我会考虑将各种网络框架用于业务应用程序.除非我有特殊的需要从头开始,否则我会使用标准框架.如果您想学习套接字级编程,那么这个问题已经为您解答.

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.

我是否与第三方打交道

在许多情况下,您可能要与需要与之通信的第三方服务器/客户端打交道.这意味着一些场景:

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

  • 他们已经定义了一个协议 - 只需使用他们的协议.
  • 您已经定义了一个协议(并且他们愿意使用它) - 再次简单地使用定义的协议
  • 他们使用标准框架(基于 WSDL 等) - 使用框架.
  • 双方都没有定义协议 - 尝试根据手头的所有因素(我在此处提到的所有因素)以及他们的能力水平(至少据您所知)确定最佳解决方案.无论如何确保双方同意并理解协议.根据经验,这可能是痛苦的,也可能是愉快的.这取决于您与谁合作.

在任何一种情况下,您都不会与第三方合作,所以这只是为了完整性而添加的.

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

我觉得我可以写更多关于这个的东西,但它已经相当长了.我希望这会有所帮助,如果您有任何具体问题,请在 Stackoverflow 上提问.

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.

回答 knoopx 问题的

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

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