2011-06-30 12 views
5

AVISO¿Ayuda de diseño para una aplicación en red?

Esta es una pregunta bastante grande, así que por favor tengan paciencia conmigo y me disculpo de antemano si esto no está claro. Con el fin de hacer que esta pregunta sea manejable, y para minimizar la confusión, omito algunas propiedades de las clases de copiar y pegar.

CONTEXTO

Estoy escribiendo una aplicación en red - un 'escritorio remoto' tipo de aplicación para que el empollón promedio de aficionado a la tecnología puede ayudar a sus amigos o vecinos con problemas del equipo. Me doy cuenta de que existe software libre y mucho más avanzado como TeamViewer, pero esta pregunta no discute la practicidad de crear este software.

términos comunes

El cliente es el nerd aficionado a la tecnología, el ayudante - el controlador. El servidor es la "víctima", el que está en apuros. El cliente suele ser el que inicia comandos en el servidor.

INFORMACIÓN

Este software es más que una aplicación de visualización/de control en directo. Quería módulos adicionales, como un módulo explorador de archivos , y un Módulo de Chat (por lo que ambos pudieran comunicarse sin necesidad de utilizar software de mensajería instantánea adicional).

Originalmente, mi enfoque para enviar y recibir mensajes a través de UDP era manual e ineficiente. (que utilizan la biblioteca Lidgren para redes UDP así que por eso mis paquetes no muestran bytes de bajo nivel como un tamaño del mensaje de cabecera.)

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) 

Este enfoque se hizo difícil de envolver mi cabeza una vez me dirijo 100 + mensajes. La fuente se volvió críptica y las modificaciones fueron una tarea ardua. Incluso al codificar el 'módulo de destino' y 'comando' como una enumeración (aún indirectamente números), todavía era difícil desarrollar la aplicación. Al recibir estos paquetes, el análisis de los argumentos específicos del comando se convertiría en una escalera de cambio ridículamente larga anidada dentro de una escalera ridículamente larga.

Más tarde decidí emplear la serialización para la estructura de mi paquete. Ahora podría diseñar cada comando como una clase, y luego serializarlo en una matriz de bytes compacta (o cadena), y luego volver a serializarlo de nuevo en su clase original y leer los argumentos como propiedades de clase. Parecía mucho más fácil, y estaba dispuesto a pagar el costo del ancho de banda para la estructura de datos en serie más grande.

Revised Packet Structure: 

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

EL PROBLEMA

Pero esta cuestión es una de diseño. No tengo problemas para serializar y deserializar mis clases. El problema radica en cómo diseño mis clases de comando/paquete. Me encuentro estancado haciendo this.Los diseños de mis clases son a continuación:

Paquete Base Clase

/// <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; } 
} 

Bienvenido Módulo de Paquetes Base (más sobre esto más adelante)

/// <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, 

} 

Explicación para una 'WelcomeModulePacket' clase:

Así que como tengo más de dos módulos, tener solo enumeración 'CommandModule' enumerando cada comando para todos los módulos, sería una enumeración realmente larga y bastante desorganizada. Es por eso que tengo diferente 'ModuleCommand' enumeración para cada módulo.

conexión de paquete de

/// <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; } 
} 

Por lo tanto, se puede ver cómo mis clases no sólo tienen dos capas de herencia, pero tres.

ClientRequestingConnectionPacket : WelcomeModulePacket : Packet 

El problema con este diseño es evidente cuando almaceno paquetes específicos como 'ClientRequestingConnectionPacket' en una estructura de datos menos específica. Por ejemplo, encolando 'ClientRequestingConnectionPacket' en un PriorityQueue genérico (para la clasificación de prioridad, recuerda cómo la clase 'Packet' tiene una propiedad 'PacketPriority'). Cuando dequeue este paquete, se quita de la cola como un paquete , no como un ClientRequestingConnectionPacket. En este caso, ya que hay un solo paquete allí, obviamente sé que el tipo original del paquete es ClientRequestingConnectionPacket, pero cuando estoy almacenando cientos de paquetes en la cola, no tengo forma de saber el original específico escribe para devolverlo.

Así que hicieron adiciones extrañas como la adición de esta propiedad:

Type UltimateType { get; set; } 

a la clase base 'Packet'. Pero quiero decir que esta 'solución' parece realmente forzada y primitiva, y no creo que sea una solución real.

Entonces, en base a ese enlace a la otra pregunta de StackOverflow anterior, ¿estoy cometiendo ese error? ¿Cómo lo arreglo? ¿Cómo se llama el término para esto? ¿Cómo debería diseñar mi aplicación?

PREGUNTA REVISADA: ¿Cuál es el patrón de diseño apropiado para las clases de paquete?

+0

Esto suena como el [patrón de la fábrica.] (Http://en.wikipedia.org/wiki/Abstract_factory_pattern) El patrón de fábrica le permitirá especificar el tipo de objeto en tiempo de ejecución. –

+0

Quizás considere la Composición (y deje expuesto el tipo compuesto) sobre Herencia. –

+0

considera editar tu pregunta. Es demasiado largo y es información casi irrelevante. La etiqueta 'programación en red' no parece relevante para su pregunta. –

Respuesta

1

Puede utilizar GetType como sugiere FishBasketGordo, o puede probar el tipo usando is

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

Cuando deserializa sus objetos, ¿qué devuelve cuando llama al GetType? Si no me equivoco, será el tipo real y específico, no el tipo base general. De esa manera, necesitaría agregar una propiedad adicional para conocer el tipo real.

Cuestiones relacionadas