УВЕДОМЛЕНИЕ
Это довольно большой вопрос, поэтому, пожалуйста, потерпите меня, и я заранее прошу прощения, если это неясно.Чтобы сделать этот вопрос более понятным и минимизировать путаницу, я опускаю некоторые свойства в классах с копированием и вставкой.
CONTEXT
Я пишусетевое приложение - приложение типа «удаленный рабочий стол», так что обычный технарь может помочь своим друзьям или соседям в решении проблем с компьютером.Я понимаю, что существует бесплатное и намного более продвинутое программное обеспечение, такое как TeamViewer, но этот вопрос не обсуждает практичность его создания.
ОБЩИЕ УСЛОВИЯ
клиент - технарь, помощник - контроллер .Сервер - это «жертва», которая терпит бедствие. клиент - это обычно тот, кто инициирует команды серверу.
ИНФОРМАЦИЯ
Это программное обеспечение больше, чем приложение для просмотра в реальном времени / управления.Мне нужны дополнительные модули , такие как Модуль проводника файлов и Модуль чата (), чтобы они оба могли общаться без необходимости использовать дополнительный обмен мгновенными сообщениямиsoftware ).
Изначально мой подход к отправке и получению сообщений через 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
ПРОБЛЕМА
Но этот вопрос одиндизайна.У меня нет проблем с сериализацией и десериализацией моих классов.Проблема заключается в том, как я проектирую свои классы команд / пакетов.Я застрял, делая это .Проекты моих классов приведены ниже:
Базовый класс пакетов
/// <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; }
}
Приветственный модуль Packet Base (подробнее об этом ниже)
/// <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':
Так как у меня более двух модулей, я имею перечисление single перечисление '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; }
к базовому классу «Пакет». Но я имею в виду, что это «решение» кажется действительно вынужденным и примитивным, и я не верю, что это реальное решение.
Итак, основываясь на этой ссылке на другой вопрос StackOverflow выше, я делаю эту ошибку? Как мне это исправить? Какой термин для этого называется? Как мне разработать свое приложение?
ПЕРЕСМОТРЕННЫЙ ВОПРОС: Какой шаблон проектирования подходит для классов Packet?