为联网应用程序设计帮助?
注意
这是一个相当大的问题,所以请耐心等待,如果不清楚,我会提前道歉。 为了使这个问题易于管理,并尽量减少混淆,我省略了复制和粘贴类的一些属性。
CONTEXT
我正在编写一个联网应用程序 – 一种“远程桌面”应用程序,以便普通技术人员可以帮助他的朋友或邻居解决计算机问题。 我意识到像TeamViewer这样的免费和更高级的软件存在,但这个问题并没有讨论创建这个软件的实用性。
共同条款
客户是技术员,帮手 – 控制器 。 服务器是“受害者”,遇险者。 客户端通常是向服务器发起命令的客户端。
信息
该软件不仅仅是实时查看/控制应用程序。 我想要额外的模块 ,例如文件资源管理器模块和聊天模块 ( 这样他们就可以在不需要使用额外的即时消息软件的情况下进行通信 )。
最初,我通过UDP发送和接收消息的方法是手动且低效的。 ( 我使用Lidgren库进行UDP网络连接,这就是为什么我的数据包不显示低级字节,如标题消息大小。 )
Original Packet Structure: Byte Index Type Purpose/Description ------------------------------------------------------ 0 byte The intended destination module (eg 1 -> Chat module) 1 byte The command for this module to perform (eg 0 -> NewChatMessage) 2 byte Encryption/compression flag 3 byte Packet priority flag (eg Cancel packets should be processed first) 4-X ? Command-specific arguments of variable type (eg 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
问题
但这个问题是设计之一。 序列化和反序列化我的类没有问题。 问题在于我如何设计我的命令/数据包类。 我发现自己坚持这样做。 我的课程设计如下:
数据包基类
/// /// The fundamental unit of network communication which can be transmitted and received from client to server. Contains the destination module and module-specific command. /// public class Packet { /// /// Specifies the module this packet is forwarded to. /// public Module DestinationModule { get; set; } /// /// Specifies whether this packet is encrypted or compressed. /// public EncryptionCompressionFlag EncryptedOrCompressed { get; set; } /// /// Specifies the packet's priority. /// public PacketPriority Priority { get; set; } }
欢迎模块数据包库(更多相关信息如下)
/// /// Base packet structure for packets specific to this module. Adds a ModuleCommand property from the base Packet class. /// public class WelcomeModulePacket : Packet { /// /// Specifies the command number this particular packet is instructing the module to execute. /// public ModuleCommand Command { get; set; } } /// /// Specifies the commands for this module. /// public enum ModuleCommand : byte { /// /// The user has just clicked the Connect button (on form's GUI) and is sending this connect packet. /// ClientRequestingConnectionPacket = 0, }
‘WelcomeModulePacket’类的说明:
因为我有两个以上的模块,只有一个枚举’CommandModule’枚举所有模块的每个命令,将是一个非常长的枚举和非常混乱。 这就是为什么我为每个模块都有一个不同的 ‘ModuleCommand’枚举。
连接包
/// /// The user has just clicked "Connect" on the Welcome form of the client. The client is now requesting a connection to the server. /// public class ClientRequestingConnectionPacket : WelcomeModulePacket { public string Username { get; set; } public string Password { get; set; } }
所以,你可以看到我的类不仅有两层inheritance,还有三层 。
ClientRequestingConnectionPacket : WelcomeModulePacket : Packet
当我将特定数据包(如“ClientRequestingConnectionPacket”)存储到不太具体的数据结构中时,此设计的问题就显而易见了。 例如,将“ClientRequestingConnectionPacket”排入通用PriorityQueue(用于优先级排序,请记住“Packet”类如何具有“PacketPriority”属性)。 当我将此数据包出列时,它将作为数据包而不是ClientRequestingConnectionPacket出列 。 在这种情况下,由于那里只有一个数据包,我显然知道数据包的原始类型是ClientRequestingConnectionPacket ,但是当我在队列中存储数百个数据包时,我没有办法知道原始的特定类型到把它扔回去。
所以我做了奇怪的添加,比如添加这个属性:
Type UltimateType { get; set; }
到’Packet’基类。 但我的意思是,这种“解决方案”看起来真的是强制性和原始性的,我不相信它是一个真正的解决方案。
那么,根据上面其他StackOverflow问题的链接,我是否犯了这个错误? 我如何解决它? 这称为什么? 我应该如何设计我的申请?
修订问题:Packet类的适当设计模式是什么?
你可以像FishBasketGordo建议的那样使用GetType
,或者你可以使用is
来测试类型
if (packet is ClientRequestingConnectionPacket) { /* do something */ } else if (packet is SomeOtherPacket) ....
反序列化对象时,调用GetType
时返回了什么? 如果我没有弄错的话,它将是实际的,特定的类型,而不是一般的基类型。 这样您就需要添加一个额外的属性来了解实际类型。