簡體   English   中英

網絡應用程序的設計幫助?

[英]Design help for a networked application?

注意

這是一個相當大的問題,所以請多多包涵,如果不清楚,我提前道歉。 為了使這個問題易於管理,並盡量減少混淆,我從復制粘貼的類中省略了一些屬性。

語境

我正在編寫一個網絡應用程序 - 一種“遠程桌面”類型的應用程序,以便普通的技術書呆子可以幫助他的朋友或鄰居解決計算機問題。 我意識到存在像 TeamViewer 這樣的免費和更高級的軟件,但這個問題並不是在討論創建這個軟件的實用性。

常用術語

客戶是技術宅,幫手 - controller 服務器是“受害者”,處於困境中的那個。 客戶端通常是向服務器發起命令的人。

信息

該軟件不僅僅是一個實時查看/控制應用程序。 我想要額外的模塊,例如文件資源管理器模塊聊天模塊這樣它們就可以在不需要使用額外的即時通訊軟件的情況下進行通信)。

最初,我通過 UDP 發送和接收消息的方法是手動且低效的。 我將 Lidgren 庫用於 UDP 網絡,這就是為什么我的數據包不顯示像 header 消息大小這樣的低級字節的原因。

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 多條消息,這種方法就變得難以理解。 來源變得神秘,修改是一件苦差事。 即使將“目標模塊”和“命令”編碼為枚舉(仍然是間接數字),開發應用程序仍然很困難。 當接收到這些數據包時,解析特定命令的 arguments 將成為一個非常長的開關梯,嵌套在一個非常長的 if-else 梯內。

后來我決定對我的數據包結構使用序列化。 I could now design each command as a class, and then serialize it into a compact byte array (or string), and then de-serialize it back into its original class and read the arguments as class properties. 這似乎容易多了,而且我願意為更大的序列化數據結構支付帶寬成本。

Revised Packet Structure:

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

問題

但這個問題是設計問題之一。 我對我的課程進行序列化和反序列化沒有問題。 問題在於我如何設計我的命令/數據包類。 我發現自己堅持這樣 我的課程設計如下:

數據包基礎 Class

/// <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' class 的說明:

因此,由於我有兩個以上的模塊,因此有一個枚舉“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; }
}

因此,您可以看到我的課程不僅有兩層 inheritance,而且還有三層

ClientRequestingConnectionPacket : WelcomeModulePacket : Packet

當我將“ClientRequestingConnectionPacket”等特定數據包存儲到不太具體的數據結構中時,這種設計的問題就很明顯了。 例如,將“ClientRequestingConnectionPacket”排入通用 PriorityQueue(對於優先級排序,請記住“Packet”class 如何具有“PacketPriority”屬性)。 當我將此數據包出列時,它作為Packet而不是ClientRequestingConnectionPacket出列。 在這種情況下,由於那里只有一個數據包,我顯然知道數據包的原始類型是ClientRequestingConnectionPacket ,但是當我在隊列中存儲數百個數據包時,我無法知道原始特定類型把它扔回去。

所以我做了一些奇怪的補充,比如添加這個屬性:

Type UltimateType { get; set; }

到“數據包”基礎 class。 但我的意思是,這個“解決方案”似乎真的很強迫和原始,我不相信它是一個真正的解決方案。

那么,基於上面另一個 StackOverflow 問題的鏈接,我是否犯了這個錯誤? 我如何解決它? 這叫什么名字? 我應該如何設計我的應用程序?

修訂后的問題:Packet 類的適當設計模式是什么?

當你反序列化你的對象時,調用GetType會返回什么? 如果我沒記錯的話,它將是實際的特定類型,而不是一般的基本類型。 這樣你就需要添加一個額外的屬性來知道實際的類型。

您可以按照 FishBasketGordo 的建議使用GetType ,也可以使用is測試類型

if (packet is ClientRequestingConnectionPacket)
{ /* do something */ }
else if (packet is SomeOtherPacket)
....

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM