为联网应用程序设计帮助?

时间:2011-06-30 03:32:05

标签: c# .net design-patterns casting

注意

这是一个相当大的问题,所以请耐心等待,如果不清楚我会事先道歉。为了使这个问题易于管理,并尽量减少混淆,我省略了复制粘贴类的一些属性。

CONTEXT

我正在编写一个联网应用程序 - 一种“远程桌面”应用程序,以便普通技术人员可以帮助他的朋友或邻居解决计算机问题。我意识到像TeamViewer这样的免费和更先进的软件存在,但这个问题并没有讨论创建这个软件的实用性。

通用条款

客户端是技术人员,帮助者 - 控制器服务器是“受害者”,遇险者。 客户端通常是向服务器发起命令的客户端。

信息

该软件不仅仅是实时查看/控制应用程序。我想要额外的模块,例如文件资源管理器模块聊天模块,这样他们就可以在不需要使用的情况下进行通信额外的即时通讯软件)。

最初,我通过UDP发送和接收消息的方法是手动且低效的。 (我使用Lidgren库进行UDP网络连接,这就是为什么我的数据包不显示低级字节,如标题消息大小。

Original Packet Structure:

Byte Index    Type      Purpose/Description
------------------------------------------------------
0             byte      The intended destination module (e.g. 1 -> Chat module)
1             byte      The command for this module to perform (e.g. 0 -> NewChatMessage)
2             byte      Encryption/compression flag
3             byte      Packet priority flag (e.g. Cancel packets should be processed first)
4-X            ?        Command-specific arguments of variable type (e.g. For this example, the argument would be the actual chat message text)

一旦我收到100多条消息,这种方法就很难包裹起来。来源变得神秘,修改是一件苦差事。即使将“目标模块”和“命令”编码为枚举(仍然是间接数字),仍然很难开发应用程序。当接收到这些数据包时,解析特定于命令的参数将成为嵌套在一个非常长的if-else梯形图中的一个可笑的长开关梯。

我后来决定对我的数据包结构采用序列化。我现在可以将每个命令设计为一个类,然后将其序列化为一个紧凑的字节数组(或字符串),然后将其反序列化为其原始类,并将参数作为类属性读取。它似乎更容易,我愿意为更大的序列化数据结构支付带宽成本。

Revised Packet Structure:

Byte Index    Type      Purpose/Description
------------------------------------------------------
0-X           String    Serialized class

问题

但这个问题是设计之一。我没有问题序列化和反序列化我的类。问题在于我如何设计我的命令/数据包类。我发现自己陷入了this。我的课程设计如下:

数据包基类

/// <summary>
/// The fundamental unit of network communication which can be transmitted and received from client to server. Contains the destination module and module-specific command.
/// </summary>
public class Packet
{
    /// <summary>
    /// Specifies the module this packet is forwarded to.
    /// </summary>
    public Module DestinationModule { get; set; }

    /// <summary>
    /// Specifies whether this packet is encrypted or compressed.
    /// </summary>
    public EncryptionCompressionFlag EncryptedOrCompressed { get; set; }

    /// <summary>
    /// Specifies the packet's priority.
    /// </summary>
    public PacketPriority Priority { get; set; }
}

欢迎模块数据包库(以下详细介绍)

/// <summary>
/// Base packet structure for packets specific to this module. Adds a ModuleCommand property from the base Packet class.
/// </summary>
public class WelcomeModulePacket : Packet
{
    /// <summary>
    /// Specifies the command number this particular packet is instructing the module to execute.
    /// </summary>
    public ModuleCommand Command { get; set; }
}

/// <summary>
/// Specifies the commands for this module.
/// </summary>
public enum ModuleCommand : byte
{
    /// <summary>
    /// The user has just clicked the Connect button (on form's GUI) and is sending this connect packet.
    /// </summary>
    ClientRequestingConnectionPacket = 0,

}

“WelcomeModulePacket”类的说明:

因为我有两个以上的模块,所以单个枚举'CommandModule'枚举 所有模块的每个命令,将是一个真的很长的枚举和相当混乱。这就是为什么我为每个模块都有一个不同的'ModuleCommand'枚举。

连接数据包

/// <summary>
/// The user has just clicked "Connect" on the Welcome form of the client. The client is now requesting a connection to the server.
/// </summary>
public class ClientRequestingConnectionPacket : WelcomeModulePacket
{
    public string Username { get; set; }
    public string Password { get; set; }
}

所以,你可以看到我的类不仅有两层继承,而且三层

ClientRequestingConnectionPacket : WelcomeModulePacket : Packet

当我将特定数据包(如“ClientRequestingConnectionPacket”)存储到不太具体的数据结构中时,此设计的问题很明显。例如,将“ClientRequestingConnectionPacket”排入通用PriorityQueue(用于优先级排序,请记住“Packet”类如何具有“PacketPriority”属性)。当我将此数据包出列时,它将作为数据包出列,而不是 ClientRequestingConnectionPacket 。在这种情况下,由于那里只有一个数据包,我显然知道数据包的原始类型是 ClientRequestingConnectionPacket ,但是当我在队列中存储数百个数据包时,我没有任何办法要知道原来的特定类型要把它丢回来。

所以我做了一些奇怪的补充,比如添加这个属性:

Type UltimateType { get; set; }

到'Packet'基类。但我的意思是,这个“解决方案”看起来真的是强迫和原始的,我不相信它是一个真正的解决方案。

所以,根据上面其他StackOverflow问题的链接,我是否犯了这个错误?我如何解决它?这称为什么?我应该如何设计我的申请?

已修订问题:数据包类的设计模式是什么?

2 个答案:

答案 0 :(得分:1)

反序列化对象时,调用GetType时返回了什么?如果我没有弄错的话,它将是实际的,特定的类型,而不是一般的基类型。这样你就需要添加一个额外的属性来了解实际的类型。

答案 1 :(得分:1)

您可以使用GetType作为FishBasketGordo建议,或者您可以使用is

来测试类型
if (packet is ClientRequestingConnectionPacket)
{ /* do something */ }
else if (packet is SomeOtherPacket)
....