socket - Multi-cliente, conectores asíncronos en c#, mejores prácticas?
tcp server c# (4)
Aconsejaría contra conexiones múltiples para la diversa información. Diseñaré algún protocolo que contenga un encabezado con información para procesar. Use la menor cantidad de recursos posible. Además, es posible que no desee enviar actualizaciones para varias posiciones y estadísticas del cliente al servidor. De lo contrario, puede terminar en una situación en la que un usuario puede modificar el envío de sus datos al servidor.
Supongamos que un usuario falsifica la ubicación de un monstruo para evitar algo. Solo actualizaría el vector de posición del usuario y las acciones. Deje que el resto de la información sea procesada y validada por el servidor.
Estoy tratando de comprender mejor los sockets tcp / ip en c #, ya que quiero desafiarme a mí mismo para ver si puedo crear una infraestructura de trabajo MMO (mundo del juego, mapa, jugadores, etc.) solo con fines educativos, ya que no tengo la intención de ser uno de esos "OMGZ va a hacer que mi MMORPG r0x0r sea mejor que WoW !!!", ya sabes de lo que estoy hablando.
De todos modos, me preguntaba si alguien puede arrojar algo de luz sobre cómo se puede abordar el diseño de este tipo de sistema y qué tipo de cosas se requieren, y qué debo tener cuidado?
Mi idea inicial era dividir el sistema en conexiones separadas de cliente / servidor con cada conexión (en su propio puerto) realizando una tarea específica, como actualizar posiciones de jugador / monstruo, enviar y recibir mensajes de chat, etc. lo que a mí me haría el procesamiento de los datos es más fácil porque no siempre se necesita poner un encabezado en los datos para saber qué información contiene el paquete.
¿Tiene sentido y es útil o simplemente estoy complicando las cosas?
sus respuestas son muy apreciadas.
Creo que necesitas gatear antes de caminar y antes de correr. Primero, obtenga el diseño del marco y las conexiones correctas, luego preocúpese por la escalabilidad. Si su objetivo es aprender la programación de C # y tcp / ip, entonces no lo haga más difícil.
Continúe con sus ideas iniciales y mantenga las secuencias de datos separadas.
Diviértete y buena suerte
Sí, creo que tienes razón sobre la conexión única y, por supuesto, el cliente no enviaría datos reales al servidor, sino simplemente comandos simples como ''avanzar'', ''girar a la izquierda'', etc. y el servidor se movería el personaje en el mapa y envíe las nuevas coordenadas de vuelta al cliente.
Si está realizando una programación a nivel de socket, independientemente de la cantidad de puertos que abra para cada tipo de mensaje, aún debe tener un encabezado de algún tipo. Incluso si es solo la longitud del resto del mensaje. Una vez dicho esto, es fácil agregar una estructura simple de encabezado y cola a un mensaje. Creo que es más fácil tener que lidiar solo con un puerto del lado del cliente.
Creo que los MMORPG modernos (y tal vez incluso los antiguos) tenían dos niveles de servidores. Los servidores de inicio de sesión que lo verifican como cliente de pago. Una vez que se hayan verificado, pasarán al servidor del juego que contiene toda la información del mundo del juego. Aun así, esto solo requiere que el cliente tenga un socket abierto, pero no impide tener más.
Además, la mayoría de los MMORPGS también encriptan todos sus datos. Si está escribiendo esto como un ejercicio por diversión, entonces esto no importará tanto.
Para diseñar / escribir protocolos en general, aquí están las cosas que me preocupan:
Endianess
¿El cliente y el servidor siempre tienen la garantía de tener la misma endianess? Si no, necesito manejar eso en mi código de serialización. Hay múltiples formas de manejar la endianess.
- Ignorarlo - Obviamente una mala elección
- Especifique la endianidad del protocolo. Esto es lo que hicieron / hicieron los protocolos más antiguos, de ahí el término de red que siempre fue big endian. En realidad, no importa qué endianes especifiques solo para especificar uno u otro. Algunos viejos programadores de redes se levantarán en armas si no usas big endianess, pero si tus servidores y la mayoría de tus clientes son pequeños endian, realmente no te estás comprando nada más que un trabajo extra al hacer big endian el protocolo.
- Marque la Endianess en cada encabezado: puede agregar una cookie que le dirá la endianess del cliente / servidor y dejar que cada mensaje se convierta según corresponda según sea necesario. ¡Trabajo extra!
- Haga que su protocolo sea independiente: si envía todo como cadenas de caracteres ASCII, entonces la independencia es irrelevante, pero también mucho más ineficiente.
De los 4 usualmente elegiría 2, y especifico que la endianess es la de la mayoría de los clientes, que ahora serán pequeños endian.
Compatibilidad hacia adelante y hacia atrás
¿El protocolo debe ser hacia adelante y hacia atrás compatible? La respuesta casi siempre debería ser sí. En tal caso, esto determinará cómo diseñar el protocolo completo en términos de control de versiones, y cómo se crea cada mensaje individual para manejar cambios menores que realmente no deberían ser parte del control de versiones. Puede punt y usar XML, pero pierde mucha eficiencia.
Para las versiones generales generalmente diseño algo simple. El cliente envía un mensaje de control de versiones que especifica que habla la versión XY, siempre que el servidor pueda admitir esa versión, devuelve un mensaje que confirma la versión del cliente y todo continúa adelante. De lo contrario, el cliente queda atrapado y finaliza la conexión.
Para cada mensaje, tiene algo como lo siguiente:
+-------------------------+-------------------+-----------------+------------------------+
| Length of Msg (4 bytes) | MsgType (2 bytes) | Flags (4 bytes) | Msg (length - 6 bytes) |
+-------------------------+-------------------+-----------------+------------------------+
La longitud, obviamente, le dice cuánto tiempo es el mensaje, sin incluir la longitud en sí. El MsgType es el tipo de mensaje. Solo dos bytes para esto, ya que 65356 son muchos tipos de mensajes para aplicaciones. Las banderas están ahí para informarle qué se serializa en el mensaje. Este campo combinado con la longitud es lo que le da su compatibilidad hacia adelante y hacia atrás.
const uint32_t FLAG_0 = (1 << 0);
const uint32_t FLAG_1 = (1 << 1);
const uint32_t FLAG_2 = (1 << 2);
...
const uint32_t RESERVED_32 = (1 << 31);
Entonces su código de deserialización puede hacer algo como lo siguiente:
uint32 length = MessageBuffer.ReadUint32();
uint32 start = MessageBuffer.CurrentOffset();
uint16 msgType = MessageBuffer.ReadUint16();
uint32 flags = MessageBuffer.ReadUint32();
if (flags & FLAG_0)
{
// Read out whatever FLAG_0 represents.
// Single or multiple fields
}
// ...
// read out the other flags
// ...
MessageBuffer.AdvanceToOffset(start + length);
Esto le permite agregar nuevos campos al final de los mensajes sin tener que revisar todo el protocolo. También asegura que los servidores y clientes antiguos ignoren los indicadores que no conocen. Si tienen que usar los nuevos indicadores y campos, simplemente cambia la versión general del protocolo.
Use un trabajo de marco o no
Existen diversos marcos de red que consideraría usar para una aplicación comercial. A menos que tuviera una necesidad específica de arañar, iría con un marco estándar. En su caso, usted desea aprender la programación a nivel de socket, por lo que esta es una pregunta ya respondida por usted.
Si uno usa un marco de trabajo, asegúrese de que atienda las dos preocupaciones anteriores, o al menos no se interponga en su camino si necesita personalizarlo en esas áreas.
Estoy tratando con un tercero
En muchos casos, puede estar tratando con un servidor / cliente de terceros con el que necesita comunicarse. Esto implica algunos escenarios:
- Ya tienen un protocolo definido: simplemente use su protocolo.
- Ya tiene un protocolo definido (y están dispuestos a usarlo) - de nuevo, simplemente use el protocolo definido
- Usan un Framework estándar (basado en WSDL, etc.) - Use el framework.
- Ninguna de las partes tiene un protocolo definido: trate de determinar la mejor solución en función de todos los factores a mano (todos los que he mencionado aquí), así como su nivel de competencia (al menos hasta donde puede ver). De todos modos, asegúrese de que ambas partes estén de acuerdo y entiendan el protocolo. Por experiencia, esto puede ser doloroso o agradable. Depende de con quién estés trabajando.
En cualquier caso, no trabajarás con un tercero, por lo que en realidad solo se agrega para completarlo.
Siento que podría escribir mucho más sobre esto, pero ya es bastante extenso. Espero que esto ayude y si tiene alguna pregunta específica solo pregunte en .
Una edición para responder la pregunta de Knoopx: