我正在开发一个应用程序,它接受原始二进制消息(非常简单,第一个字节是消息类型,其余是有效负载),然后用它做一些事情。 我想要完成的事情是确保网络服务从应用程序的其余部分中抽象出来,以便允许时不时地修改协议,而不会过多影响应用程序的其余部分。
该应用程序的上下文是一个非常简单的客户端-服务器游戏,我现在正在为此做客户端工作。
不过我现在有点挣扎。 我需要找到一种优雅的方法来将连接放入某种翻译器/适配器服务中,该服务返回漂亮的对象(我认为)。 这些对象将被放入队列中,等待应用程序的其余部分使用。 我面临的问题或多或少是这个构造(伪代码):
让我们假设每条消息都是 20 个字节,所以我可以处理每 20 个字节调用这个函数:
public Message GetMessage(byte[] buffer)
{
switch(buffer[0])
{
case 1:
return Message1(...);
case 2:
return Message2(...);
.....
case n:
return MessageN(...);
}
}
显然,我将使用枚举或常量是的,但这不是困扰我的事情。
事情是这样的。 我认为我有大约 50 种消息类型,这意味着我将得到包含 50 种情况的 switch 语句。 我真的想不出一种正确的方法来将其切成更小的部分,这将导致一个巨大的、容易出错的方法。 我想知道是否有任何模式可以让这变得更容易,因为我找不到任何模式。
感谢您提前输入!
I'm working on an app which takes in a raw binary message (very simple, first byte is the message type, rest is payload), and then does something with it. The thing I'm trying to accomplish is making sure that the networking service is abstracted away from the rest of the app, to allow for modifying the protocol now and then without affecting the rest of the application too much.
The context of the application is a very simple client-server game, for which I am doing the client work now.
I'm kinda struggling now though. I need to find an elegant way to just throw a connection into some sort of translator/adapter service, which returns pretty objects (I think). Those objects will be thrown in a queue awaiting consumption by the rest of the app. The problem I'm facing is more or less this construct (pseudo code):
Let's assume each message is the 20 bytes, so I can deal with calling this function for each 20 bytes:
public Message GetMessage(byte[] buffer)
{
switch(buffer[0])
{
case 1:
return Message1(...);
case 2:
return Message2(...);
.....
case n:
return MessageN(...);
}
}
Obviously, I'll use an enum or constants for the case, but that's not the thing that's bugging me.
Here's the thing. I think I've got about 50 message types, which would mean I'll get a switch statement with 50 cases. I can't really think of a proper way to cut this up into smaller pieces, which will result in a huge, error prone method. I was wondering if there's any patterns to make this easier, as I couldn't find any.
Thanks for the input in advance!
发布评论
评论(4)
您可以有一个包含 50 个函数指针(即 C# 委托)的数组,您可以使用第一个字节的值对其进行索引,或者有一个委托字典,其键是第一个字节的值。 但这只是编写 switch 语句的另一种方式。
不过,它更加分布式:例如,当您创建新的消息类型(可能是新源文件中的新类)时,您可以调用现有方法将新委托添加到委托的静态集合中。
You could have an array of 50 function pointers (i.e. C# delegates) which you index using the value of first byte, or a dictionary of delegates whose key is the the value of first byte. That's merely another way of writing a switch statement though.
It's more distributed though: for example, when you create a new message type (which may be a new class in a new source file), then instead of editing source code to add a new case to a big switch statement, you can call an existing method to add a new delegate to the static collection of delegates.
虽然不完全是 C# 解决方案,但我最近也处理过类似的情况。
我的解决方案是使用 F#,这使它变得更容易。
例如,我的代码看起来像这样
它不是完美的解决方案,但看起来比 c# 中的 switch 语句好很多。
所以我们整个应用程序是用csharp编写的,但是消息解析器是用fsharp编写的。
供参考:
我们有几个接口:
IDataTransportServer - 负责通过 RS232 或 TCP/IP 接收数据
IDataProcessor - 负责解析二进制数据并将其转换为 Message 类的实例
IMessageProcessor - 负责处理消息(这是 fsharp 模块)
我不知道如果这对您有用,但只是想让您知道
我们如何处理此类问题。
While not exactly a C# solution, I have dealt with a similar situation recently.
My solution was to use F# which makes it a lot easier.
For example, my code looks like this
It's not the perfect solution, but looks a lot better than the switch statement in c#.
So our entire application is written in csharp, but the message parser is written in fsharp.
FYI:
We have several interfaces:
IDataTransportServer - Responsible for receiving data via RS232 or TCP/IP
IDataProcessor - Responsible for parsing binary data and turning it into instances of the Message class
IMessageProcessor - Responsible for processing messages (this is the fsharp module)
I have no idea if this is useful for you, but just wanted to let you know
how we deal with this kind of problem.
我有一些 Java 代码可以做到这一点。 希望您可以轻松地翻译成 C#。 本质上,我有一个
messageMap
集合:这是从消息 ID 到其对应的
Message
类的映射。 我为每种不同的消息类型调用一次addMessage
:然后,当消息到达时,我从线上读取消息 ID,查找我需要在
Message
类>messageMap,然后使用反射创建该类的实例。这里
newInstance()
调用默认构造函数。我在具有不同消息的多个应用程序中使用了这个通用消息处理代码。 每个都只有一个漂亮、简单的代码块,用于注册不同的消息,如下所示:
I have some Java code which does this. Hopefully you can easily translate to C#. Essentially, I have a
messageMap
collection:This is a map from message IDs to their corresponding
Message
classes. I calladdMessage
once for each different message type:Then when a message arrives I read the message ID off the wire, look up the
Message
class I need to instantiate inmessageMap
, and then use reflection to create an instance of that class.Here
newInstance()
calls the default constructor.I've used this generic message-handling code across multiple applications with different messages. Each one just has a nice, simple block of code registering the different messages like so:
嗯,当然有很多方法。 标准的是将函数存储在字典中。 在函数式语言中,您可能会编写类似“
我不确定您的 C/C++/C# 版本是什么”之类的内容。 如果你不能在那里放置函数,那么就放置指向函数的指针。 如果你的一些函数非常小,在某些语言中你可以用 lambda 来放置:
我会做更多的优化。 请注意,对于每条消息,您都有一个常量和一个函数名称。 不过,您不必重复:
但如果您想在哲学上正确,您可以为处理程序创建单独的类:
Well, there are certainly many ways. The standard one is to store functions in a dictionary. In functional languages you would write something like
I'm not sure what's your version of C/C++/C#. If you can't put functions there, then put pointers to functions. If some of your functions are very small, in some languages you can put then right there with
lambda
:There are more optimizations that I would do. See, for every message you have a constant and a function name. You don't have to repeat, though:
But if you want to be philosophically correct, you can have separate classes for handlers: