UDP sem bloqueio ou um thread separado para recebimento?

10

Estou criando um jogo multiplayer (para menos de 64 jogadores). Eu já decidi ter um thread separado para o loop da rede, mas queria saber se seria melhor criar um thread extra para receber UDP ou definir o soquete de recebimento no modo sem bloqueio (sem thread extra).

Ou é melhor usar outro método como soquetes assíncronos? Métodos melhores são sempre bem-vindos!

Yannick Lange
fonte

Respostas:

6

Ok, primeiro de tudo, se você tem algo e está funcionando, geralmente é uma boa ideia deixar as coisas assim. Por que consertar o que não está quebrado?

Mas se você está tendo problemas e realmente gostaria de reescrever seu código de rede, acho que você tem quatro opções principais:

  1. Código de bloqueio multithread (o que você está fazendo agora)
  2. Soquetes sem bloqueio com notificação acionada por nível
  3. Soquetes sem bloqueio com notificação de alteração de prontidão
  4. Soquetes assíncronos

Tendo escrito muitos clientes e servidores multiplayer (de ponto a ponto para multiplayer em massa), gosto de pensar que a opção 2 leva à menor complexidade, com desempenho muito bom, tanto para as partes do servidor quanto para o cliente. Como um segundo próximo, eu iria com a opção 4, mas isso geralmente exige que você repense todo o seu programa, e acho isso útil principalmente para servidores e não para clientes.

Em particular, eu gostaria de aconselhar contra o bloqueio de soquetes em um ambiente multithread, pois isso geralmente leva ao bloqueio e a outros recursos de sincronização que não apenas aumentam bastante a complexidade do código, mas também podem prejudicar seu desempenho, pois alguns threads aguardam outras.

Mas, acima de tudo, a maioria das implementações de soquete (e a maioria das implementações de E / S) não bloqueiam no nível mais baixo. As operações de bloqueio são simplesmente fornecidas para simplificar o desenvolvimento de programas triviais. Quando um soquete está bloqueando, a CPU nesse encadeamento fica completamente ociosa; então, por que criar uma abstração sem bloqueio sobre a abstração de bloqueio sobre uma tarefa já sem bloqueio?

A programação de soquetes sem bloqueio é um pouco assustadora se você ainda não o experimentou, mas é bem simples e, se você já está fazendo uma pesquisa de entrada, já tem a mentalidade de fazer soquetes sem bloqueio.

A primeira coisa que você deseja fazer é definir o soquete para não-bloqueio. Você faz isso com fcntl().

Depois disso, antes de o fazer send(), recv(), sendto(), recvfrom(), accept()( connect()é um pouco diferente) ou outras chamadas que podem bloquear o thread, você chama select()no soquete. select()informa se uma operação de leitura ou gravação subseqüente pode ou não ser executada no soquete sem bloquear. Se for esse o caso, você pode executar com segurança a operação desejada e o soquete não bloqueará.

Incluir isso em um jogo é bastante simples. Se você já possui um loop de jogo, por exemplo, assim:

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

você pode alterá-lo para ficar assim:

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

Onde

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

e

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

Em termos de recursos, o único livro que acho que todo mundo deve ter em suas estantes, mesmo que seja o único livro que têm, é " Unix Network Programming, Vol. 1 ", do falecido Richard Stevens. Não importa se você executa o Windows ou outro SO ou programação de soquete de idioma. Não pense que entende soquetes até ler este livro.

Outro recurso onde você pode encontrar uma visão geral das soluções disponíveis em termos de programação de múltiplos soquetes (principalmente relevantes para a programação de servidores) é esta página .

Panda Pajama
fonte
Acabei de iniciar meu mecanismo de jogo, então reescrever não é um problema. Esta resposta foi realmente útil e obrigado pela recomendação do livro. Você também conhece um livro mais específico sobre redes em jogos?
precisa
3
@YannickLange: Não, mas eu não recomendo que você procure livros específicos de jogos, já que o networking é praticamente independente de gênero, e também nenhum outro livro está no nível do livro de Stevens. No entanto, parabéns pelo uso do UDP, pois o uso de um protocolo orientado a mensagens é uma boa idéia ao escrever programas orientados a mensagens (como a maioria dos jogos). Muitas pessoas acabam escrevendo uma abstração de mensagem pelo TCP, que por si só é uma abstração orientada a fluxo construída sobre um protocolo orientado a mensagens (IP).
Panda Pyjama
-1

Você não escreveu qual linguagem de programação está usando, mas a maioria das linguagens fornece boas estruturas de soquete assíncronas, que geralmente utilizam recursos do sistema operacional subjacente.

Quando você escreve sua própria implementação encadeada com base em soquetes de bloqueio (lembre-se: quando você tem vários clientes, precisa de um encadeamento para cada soquete), você apenas reinventa o que a estrutura de soquete assíncrona já fornece.

Então, eu recomendo que você use soquetes assíncronos.

Philipp
fonte
4
por que ele precisa de um thread para cada soquete? Especialmente com soquetes assíncronos? Muitos servidores pesados ​​usam um modelo de "pool de threads" para processar dados de soquete, onde cada thread atende N clientes e eles são gerados ou eliminados conforme necessário. Apenas pense que esta resposta precisa de uma melhoria: D
Grimshaw
@ Grimshaw Acho que você não entendeu minha resposta. Acho que agora esclareço que seria necessário um modelo multiencadeado ao usar soquetes de bloqueio.
315
@ Philipp Eu não disse qual linguagem de programação eu uso porque, eu pensei que não era realmente relevante para a pergunta (eu uso c ++ a propósito). Agradeço sua recomendação de usar soquete assíncrono. Você conhece um livro / página da web recomendado para aprender esses tipos de métodos em jogos (mecanismos)?
precisa
@Grimshaw: E como cada thread atende os Nclientes? Se você está bloqueando e deseja garantir que cada soquete obtenha seus dados processados, independentemente do estado dos demais soquetes, é necessário ter um encadeamento por soquete. Isso, ou não use soquetes de bloqueio.
Panda Pyjama
Meu comentário foi à primeira versão da sua resposta, entretanto, é editado e corrigido, esqueça o que eu disse: D
Grimshaw