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:
- Código de bloqueo multiproceso (lo que está haciendo ahora)
- Zócalos sin bloqueo con notificación activada por nivel
- Zócalos sin bloqueo con notificación de cambio de preparación
- 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 .