UDP sin bloqueo o un hilo separado para recibir?

10

Estoy creando un juego multijugador (para menos de 64 jugadores). Ya decidí tener un subproceso separado para el bucle de red, pero me preguntaba si sería mejor crear un subproceso adicional para recibir UDP o establecer el socket de recepción en no bloqueo (sin subproceso adicional).

¿O es mejor usar otro método como Asynchronous Sockets? ¡Los mejores métodos son siempre bienvenidos!

Yannick Lange
fuente

Respuestas:

6

Bien, antes que nada, si tienes algo y está funcionando, generalmente es una buena idea dejarlo así. ¿Por qué arreglar lo que no está roto?

Pero si tiene problemas y realmente desea reescribir su código de red, creo que tiene cuatro opciones principales:

  1. Código de bloqueo multiproceso (lo que está haciendo ahora)
  2. Zócalos sin bloqueo con notificación activada por nivel
  3. Zócalos sin bloqueo con notificación de cambio de preparación
  4. Enchufes asincrónicos

Después de haber escrito muchos clientes y servidores multijugador (de igual a igual a multijugador masivo), me gusta pensar que la opción 2 conduce a la menor complejidad, con un rendimiento bastante bueno, tanto para el servidor como para las partes del juego del cliente. Como segundo final, elegiría la opción 4, pero eso generalmente requiere que reconsidere todo su programa, y ​​en su mayoría lo encuentro útil para servidores y no para clientes.

En particular, me gustaría aconsejar que no se bloqueen los sockets en un entorno multiproceso, ya que esto generalmente lleva al bloqueo y otras características de sincronización que no solo aumentan en gran medida la complejidad del código, sino que también pueden degradar su rendimiento, ya que algunos hilos esperan otros.

Pero, sobre todo, la mayoría de las implementaciones de socket (y la mayoría de las implementaciones de E / S) no se bloquean en el nivel más bajo. Las operaciones de bloqueo simplemente se proporcionan para simplificar el desarrollo de programas triviales. Cuando un socket está bloqueando, la CPU en ese hilo está completamente inactiva, entonces, ¿por qué construir una abstracción sin bloqueo sobre la abstracción de bloqueo sobre una tarea que ya no es de bloqueo?

La programación de socket sin bloqueo es un poco desalentador si no lo ha intentado, pero resulta que es bastante simple, y si ya está realizando encuestas de entrada, ya tiene la mentalidad de hacer sockets sin bloqueo.

Lo primero que debe hacer es configurar el socket para que no se bloquee. Haces eso con fcntl().

Después de eso, antes de hacerlo send(), recv(), sendto(), recvfrom(), accept()( connect()es un poco diferente) o de otras llamadas que podrían bloquear el hilo, se llama select()en el zócalo. select()le indica si una operación de lectura o escritura posterior se puede realizar en el socket sin que se bloquee. Si ese es el caso, puede hacer la operación que desee de manera segura y el zócalo no se bloqueará.

Incluir esto en un juego es bastante simple. Si ya tienes un bucle de juego, por ejemplo así:

while game_is_running do
    poll_input()
    update_world()
    do_sounds()
    draw_world()
end

podrías cambiarlo para que se vea así:

while game_is_running do
    poll_input()
    read_network()
    update_world()
    do_sounds()
    write_network()
    draw_world()
end

dónde

function read_network()
    while select(socket, READ) do
        game.net_input.enqueue(recv(socket))
    end
end

y

function write_network()
    while not game.net_output.empty and select(socket, WRITE) do
        send(socket, game.net_output.dequeue())
    end
end

En términos de recursos, el único libro que creo que todos deben tener en sus estanterías, incluso si es el único libro que tienen, es " Unix Network Programming, Vol. 1 " del difunto Richard Stevens. No importa si haces Windows u otro sistema operativo o programación de socket de idioma. No pienses que entiendes los enchufes hasta que leas este libro.

Otro recurso en el que puede encontrar una descripción general de las soluciones disponibles en términos de programación de sockets múltiples (principalmente relevante para la programación del servidor) es esta página .

Pijama Panda
fuente
Acabo de iniciar mi motor de juego, por lo que reescribir no es un problema. Esta respuesta fue realmente útil y gracias por la recomendación del libro. ¿Conoces también un libro que sea más específico sobre las redes en los juegos?
Yannick Lange
3
@YannickLange: No, pero no recomendaría que busques libros específicos para juegos, ya que la creación de redes es bastante independiente del género, y tampoco ningún otro libro está al nivel del libro de Stevens. Sin embargo, felicitaciones por usar UDP, ya que usar un protocolo orientado a mensajes es una buena idea al escribir programas orientados a mensajes (como la mayoría de los juegos). Muchas personas tienden a terminar escribiendo una abstracción de mensajes sobre TCP, que es en sí misma una abstracción orientada a la transmisión construida sobre un protocolo orientado a mensajes (IP).
Panda Pyjama
-1

No escribió qué lenguaje de programación está utilizando, pero la mayoría de los lenguajes proporcionan buenos marcos de socket asíncronos que a menudo utilizan características del sistema operativo subyacente.

Cuando escribe su propia implementación de subprocesos basada en sockets de bloqueo (recuerde: cuando tiene varios clientes, necesita un subproceso para cada socket), simplemente va a reinventar lo que ya proporciona el marco de socket asíncrono.

Por lo tanto, le recomendaría que use sockets asíncronos.

Philipp
fuente
44
¿Por qué necesita un hilo para cada zócalo? ¿Especialmente con tomas asíncronas? Muchos servidores pesados ​​utilizan un modelo de "grupo de subprocesos" para procesar datos de socket, donde cada subproceso sirve a N clientes y se generan o eliminan según sea necesario. Solo piense que esta respuesta necesita una mejora: D
Grimshaw
@ Grimshaw Creo que entendiste mal mi respuesta. Creo que ahora he aclarado que se requeriría un modelo de subprocesos múltiples al usar enchufes de bloqueo.
Philipp
@Philipp No dije qué lenguaje de programación utilizo porque pensé que no era realmente relevante para la pregunta (por cierto, uso c ++). Le agradezco su recomendación de utilizar un socket asíncrono. ¿Conoces un libro / página web recomendada para aprender este tipo de métodos en juegos (motores)?
Yannick Lange
@Grimshaw: ¿Y cómo cada hilo sirve a los Nclientes? Si está bloqueando y desea asegurarse de que cada socket reciba sus datos sin importar el estado del resto de los sockets, debe tener un subproceso por socket. Eso, o no use enchufes de bloqueo.
Panda Pyjama
Mi comentario fue sobre la primera versión de su respuesta, mientras tanto, la editó y corrigió, olvide lo que dije: D
Grimshaw