Recursos para estabelecer uma conexão entre participantes em um jogo de rede ponto a ponto

Essa é uma coleção de informações necessárias para implementar o estágio de estabelecimento de uma conexão entre os participantes de um jogo de rede ponto a ponto usando o protocolo UDP.

O artigo foi desenvolvido para desenvolvedores de jogos iniciantes. Tentei escrever um artigo que eu gostaria de ler no momento em que estava começando a entender esse tópico: para que todas as nuances necessárias sejam coletadas em um só lugar, mas ao mesmo tempo não exista nada de supérfluo, em linguagem simples e com imagens visuais. Talvez alguém venha a calhar.

É improvável que desenvolvedores de jogos experientes encontrem algo novo para si aqui. Mas serei grato pelos comentários e comentários.



Jogo em rede com arquitetura ponto a ponto


  • Cada jogador armazena todo o estado do mundo do jogo e o processa de forma síncrona com outros jogadores. Cada jogador passa as ações do usuário para todos os outros jogadores. O jogador principal que coleta os outros jogadores é chamado servidor e o restante são clientes. O servidor é o principal apenas na fase de coleta de jogadores. E durante o jogo não há computador principal.
  • Essa abordagem possui os seguintes recursos:
    • O tráfego não depende da complexidade do mundo do jogo, mas apenas do número de jogadores. Nesse modo, as estratégias em tempo real geralmente funcionam, onde você precisa processar milhares de unidades.
    • O volume de tráfego é da ordem de N², onde N é o número de jogadores. Portanto, essa abordagem é aplicável apenas a jogos com um pequeno número de jogadores.
    • Como os dados são transmitidos diretamente entre jogadores sem um servidor intermediário, os atrasos na transmissão (atrasos) são mínimos. Mas se pelo menos um dos jogadores tiver problemas de comunicação, isso afetará todos os jogadores.
    • É necessário estabelecer canais de comunicação entre todos os players. Mas se os players estiverem em redes locais diferentes, isso nem sempre é possível.
  • Você pode usar TCP ou UDP para transferir dados no jogo.
    • O TCP (Transmission Control Protocol) fornece entrega confiável de fluxos de bytes. Isso simplifica a implementação do jogo, mas não há controle sobre os atrasos na transmissão de dados.
    • O UDP (User Datagram Protocol) é um protocolo simples de transferência de pacotes sem garantia de entrega. Porém, devido à sua simplicidade, o UDP é usado em sistemas em tempo real quando é inaceitável aguardar pacotes atrasados ​​ou perdidos. O uso do UDP pode reduzir os atrasos no jogo, mas complica a implementação do jogo.
    Este artigo descreve o uso do UDP.

Estabelecendo uma conexão na rede local


  • Para estabelecer uma conexão entre jogadores, o cliente precisa saber o endereço IP do servidor e a porta que o programa do jogo escuta.


    • Por exemplo, o computador do jogador A tem um endereço IP 192.168.1.2 na rede local. O jogador A inicia o programa do jogo em seu computador no modo servidor e o programa escuta na porta 50120. O jogador A, de alguma forma, comunica essas informações ao jogador B.
    • O computador do jogador B tem um endereço IP 192.168.1.5 na rede local. O jogador B inicia o programa do jogo em seu computador no modo cliente e o programa ocupa a porta 50150. O jogador B insere o endereço do servidor 192.168.1.2►0120 no programa do jogo e o programa envia uma solicitação ao servidor no endereço especificado.
    • O servidor está no modo de espera e, após receber uma solicitação do jogador B, descobrirá seu endereço 192.168.1.5►0150. Assim, o cliente e o servidor estabeleceram uma conexão e podem iniciar o jogo.
  • Se vários clientes estiverem conectados ao servidor, o servidor deverá enviar a cada um deles os endereços de outros clientes.


    No exemplo da figura, o servidor A envia ao cliente B o endereço do cliente C (192.168.1.6►0160) e o cliente C envia o endereço do cliente B (192.168.1.5►0150). Assim, todos os jogadores serão capazes de estabelecer conexões "cada uma com cada uma".
  • Cada vez que o jogo inicia, o servidor pode solicitar qualquer porta livre do sistema operacional. Mas é mais conveniente usar a mesma porta todas as vezes, para que o cliente não precise inserir uma nova porta todas as vezes.

    Todas as portas são divididas em três faixas:
    • [0, 1023] - bem conhecido (sistêmico).
    • [1024, 49151] - registrado (usuário). A porta do servidor deve ser selecionada neste intervalo.
    • [49152, 65535] - dinâmico (privado). Aqui, o sistema operacional aloca portas temporárias para programas.

    Na Wikipedia, você pode ver uma lista de portas reservadas . Em seguida, por exemplo, a porta 49094 para o servidor do jogo é selecionada.
  • Um computador pode ter várias interfaces de rede, reais (Ethernet, WiFi) e virtuais (VPN). Cada interface de rede possui seu próprio endereço IP. O programa pode fornecer ao usuário do servidor a oportunidade de selecionar a interface de rede pela qual ele aguardará as solicitações do cliente. Mas é conveniente usar um endereço IP especial curinga 0.0.0.0 . Se o programa abrir um soquete com esse endereço IP, ele ouvirá a porta especificada para todas as interfaces de rede deste computador. Assim, o jogador não precisa pensar qual interface de rede escolher.
  • Você também pode ajudar o cliente e determinar automaticamente o endereço IP do servidor. Se o cliente enviar uma solicitação para um endereço IP de broadcast especial (broadcast) , todos os computadores da rede local o receberão.

    Por exemplo, se o endereço de rede for 192.168.1.0, a máscara de sub-rede for 255.255.255.0, o endereço IP de broadcast será 192.168.1.255.


    Se todos os servidores de jogos estiverem escutando na porta 49094, o pacote enviado para o endereço 192.168.1.255-00-009094 será recebido por todos os servidores de jogos nesta rede. Cada servidor enviará uma confirmação ao remetente. E assim o cliente receberá uma lista de todos os servidores de jogos em sua rede e poderá selecionar o servidor de que precisa.
  • Se todos os players estiverem na mesma rede local, o estabelecimento de conexões “cada uma com cada uma” será bastante simples. Pode haver problemas com o acesso do cliente à porta do servidor devido ao Firewall. Mas isso depende do sistema operacional e das configurações de segurança.

Estabelecendo uma conexão de uma rede local com um servidor na Internet


  • Como regra, os computadores não estão conectados diretamente à Internet, mas através de um roteador. E, como regra, o roteador realiza a conversão de endereços de rede (NAT, Network Address Translation) .

    Por exemplo, o cliente C está na rede local e tem acesso à Internet através do roteador B, enquanto o servidor A tem um endereço IP público na Internet. Deixe que eles tenham os seguintes endereços:


    • O endereço da Internet do servidor A é 203.0.113.2. O programa do servidor escuta na porta 49094.
    • O endereço do roteador B na Internet é 203.0.113.5. O endereço do roteador B na rede local é 192.168.1.1.
    • O endereço do cliente C na rede local é 192.168.1.5. O programa cliente ocupa a porta 50150 e envia um pacote ao servidor em 203.0.113.2-00-009094.
    • O roteador B é o gateway padrão em sua rede local. Ou seja, todos os pacotes com endereços que não pertencem à rede local atual são enviados a ele.
    • O roteador possui uma tabela de conversão de endereços: (endereço interno: porta interna) - (endereço externo: porta externa). Tendo recebido um pacote do cliente para o servidor 203.0.113.2-00-009094, o roteador seleciona qualquer porta externa livre, por exemplo 52050, e cria uma entrada na tabela de conversão de endereços: 192.168.1.5►0150 - 203.0.113.5►2050.
    • O roteador substitui o endereço interno do remetente 192.168.1.5/100150 no pacote pelo endereço externo 203.0.113.5/102050 e transfere o pacote alterado para o servidor.
    • O servidor recebe um pacote com o endereço do remetente 203.0.113.5► 2020 e envia uma resposta para esse endereço, ou seja, para o roteador.
    • Depois de receber o pacote do servidor, o roteador procura o endereço do destinatário em sua tabela de conversão de endereços, executa a substituição reversa do endereço externo do destinatário 203.0.113.5►2050 pelo endereço interno 192.168.1.5►0150 e envia o pacote para esse endereço, ou seja, o cliente.
    • Se o cliente enviar pacotes subsequentes para o servidor, o roteador examinará o endereço do remetente para verificar se esse registro já existe na tabela e, se houver, ele usará a porta externa alocada anteriormente, neste caso 52050.
    • Assim, o cliente e o servidor estabeleceram uma conexão através do NAT e podem iniciar o jogo. Mas o servidor não conhece o endereço interno do cliente em sua rede local e considera que o cliente é um roteador.
    • A entrada na tabela de conversão de endereços é válida por um determinado período de tempo, geralmente de 1 a 3 minutos. Portanto, o cliente e o servidor precisam trocar pacotes periodicamente para que o registro que os conecte não seja excluído. Se o cliente enviar um novo pacote ao servidor após a exclusão do registro, uma porta externa diferente poderá ser alocada para o novo registro no roteador e, para o servidor, será outro cliente com um endereço diferente.
  • Como regra, o roteador do usuário não está conectado diretamente à Internet, mas está localizado na rede interna do provedor da Internet. Ou seja, o cliente está por trás de dois NATs.

    Por exemplo, assim:


    Ou seja, é realizada uma conversão dupla de endereços de rede.
  • Existem diferentes tipos de NAT:
    • NAT de cone completo


      • Depois que uma entrada é criada na tabela de conversão de endereços (endereço interno: porta interna) - (endereço externo: porta externa), todos os pacotes do remetente (endereço interno: porta interna) são transmitidos através de (endereço externo: porta externa) para qualquer endereço de destinatário.
      • Qualquer servidor externo pode enviar pacotes para (endereço interno: porta interna), enviando pacotes para (endereço externo: porta externa).
      Por exemplo, o cliente C envia pacotes para os servidores A e D usando o mesmo endereço externo 203.0.113.5► 202050. Se o servidor E enviar um pacote para este endereço 203.0.113.5► 202050, o roteador o passará para o cliente C.
    • NAT de cone com restrição de endereço.


      • Depois que uma entrada é criada na tabela de conversão de endereços (endereço interno: porta interna) - (endereço externo: porta externa), todos os pacotes do remetente (endereço interno: porta interna) são transmitidos através de (endereço externo: porta externa) para qualquer endereço de destinatário.
      • Um servidor externo (endereço do servidor: porta do servidor) pode enviar pacotes para (endereço interno: porta interna), enviando pacotes para (endereço externo: porta externa) somente se (endereço interno: porta interna) tiver enviado pacotes anteriormente para (endereço do servidor: qualquer porta).
      Por exemplo, o cliente C envia pacotes para os servidores A e D usando o mesmo endereço externo 203.0.113.5► 202050. O servidor D pode enviar um pacote para o cliente C através do endereço de roteador 203.0.113.5► 202050 a partir de qualquer uma de suas portas. Mas o roteador não passa pacotes do servidor E.
    • Cone com restrição de porta NAT.


      • Depois que uma entrada é criada na tabela de conversão de endereços (endereço interno: porta interna) - (endereço externo: porta externa), todos os pacotes do remetente (endereço interno: porta interna) são transmitidos através de (endereço externo: porta externa) para qualquer endereço de destinatário.
      • Um servidor externo (endereço do servidor: porta do servidor) pode enviar pacotes para (endereço interno: porta interna), enviando pacotes para (endereço externo: porta externa) somente se (endereço interno: porta interna) tiver enviado pacotes anteriormente para (endereço do servidor: porta servidor).
      Por exemplo, o cliente C envia pacotes para os servidores A e D usando o mesmo endereço externo 203.0.113.5► 202050. O roteador não passará pacotes do servidor E ou de outra porta D.
    • NAT simétrico (NAT simétrico).


      • Se o mesmo remetente interno (endereço interno: porta interna) enviar pacotes para diferentes destinatários (endereço do servidor: porta do servidor), uma porta externa separada será alocada para cada endereço de destinatário e uma entrada separada será usada na tabela de conversão de endereços.
      • Somente o servidor externo (endereço do servidor: porta do servidor) que recebeu o pacote do remetente interno (endereço interno: porta interna) pode enviar o pacote de volta.
      Por exemplo, o cliente C envia pacotes para o servidor A usando um endereço externo 203.0.113.5► 202050 e para o servidor D usando outro endereço externo 203.0.113.5. O roteador não passará pacotes do servidor E ou de outra porta D.
  • Se vários clientes estiverem conectados ao servidor do jogo, para um jogo ponto a ponto, você precisará estabelecer uma conexão diretamente entre cada par de clientes ignorando o servidor.

    Por exemplo, dois clientes C e E conectados ao servidor A:


    • O cliente C tem um endereço interno 192.168.1.5/100150 e um endereço externo 203.0.113.5/102050.
    • O cliente E tem um endereço interno 192.168.2.5:50250 e um endereço externo 203.0.113.6-062060.
    • O servidor deve informar a cada cliente o endereço externo do outro cliente.
    • Normalmente, os roteadores usam um NAT do tipo cone cone com restrição de porta. O roteador passa pacotes para o cliente apenas do remetente (endereço IP e porta) para o qual o cliente enviou pacotes anteriormente. E se o cliente C enviar um pacote para o cliente E, o roteador D não perderá esse pacote.
    • Nesse caso, o método de perfuração UDP é usado. Ambos os clientes devem enviar pacotes UDP para o outro. Assim que o cliente C enviar um pacote ao cliente E, o roteador B estará pronto para receber pacotes do cliente E. Da mesma forma, assim que o cliente E enviar um pacote ao cliente C, o roteador D estará pronto para receber pacotes do cliente C. Os primeiros pacotes do cliente que iniciaram transmitir primeiro será perdido. Mas assim que o segundo cliente também começar a enviar pacotes, os dois poderão trocar pacotes.
    • Mas se o roteador usar NAT simétrico, uma nova porta externa será alocada para cada destinatário. E, como regra, não há como descobrir qual porta o roteador alocou. Portanto, se pelo menos um dos roteadores usar o NAT simétrico, será impossível estabelecer uma conexão entre os clientes.
  • Se dois clientes estiverem na mesma rede local, eles conhecerão apenas os endereços externos um do outro.


    Por exemplo, o cliente C envia um pacote para o cliente D em seu endereço externo 203.0.113.5Point2051. O roteador B deve processar esse pacote como se tivesse sido recebido de uma rede externa, substitua o endereço do destinatário 203.0.113.5► 20201 pelo endereço interno do cliente D 192.168.1.6►0060 e envie o pacote ao cliente D de volta à rede local. Essa função do roteador é chamada de loopback NAT ( hairpinning NAT ). Se o loopback NAT estiver desativado no roteador, será impossível estabelecer uma conexão entre os clientes.
  • Os clientes podem estar localizados em redes locais diferentes, mas têm acesso à Internet por meio de um provedor comum.


    Se o loopback NAT estiver desativado no equipamento do provedor de serviços da Internet, será impossível estabelecer uma conexão entre os clientes.
  • O que devo fazer se o NAT simétrico for usado no roteador ou o loopback do NAT estiver desativado?

    Pelo que entendi, a única maneira confiável de resolver esses problemas é transferir pacotes não diretamente entre clientes, mas através de um servidor. É necessário implementar essa função no programa do jogo dentro da estrutura da arquitetura ponto a ponto ou implementar a arquitetura cliente-servidor.

    Ou você pode usar programas de terceiros para criar uma VPN, por exemplo, LogMeIn Hamachi .

Estabelecendo uma conexão entre LANs na Internet


  • Como regra, nenhum dos players possui um endereço IP público e estão em redes locais diferentes atrás do NAT.


    Por exemplo, nesse esquema, o cliente C enviará pacotes ao servidor A em seu endereço externo 203.0.113.2-022020, no qual a porta é selecionada pelo roteador B. Portanto, escolher a porta interna do servidor A não importa e você pode selecionar qualquer porta. Nesse caso, em vez da porta 49094, o servidor A pode solicitar qualquer porta livre do sistema operacional, por exemplo 50120.
  • Para que o servidor A e o cliente C estabeleçam uma conexão, primeiro, cada um deles precisa descobrir de alguma forma seu endereço externo.

    Você pode usar o protocolo STUN (Utilitários de transferência de sessão para NAT) para isso .


    O protocolo STUN permite que o cliente localizado atrás do NAT determine seu endereço IP e porta externos.

    As mensagens STUN são enviadas em pacotes UDP.

    O cliente pode acessar qualquer um dos servidores STUN públicos. Uma lista de servidores públicos STUN pode ser encontrada na Wikipedia . Ou pesquise "lista pública de servidores STUN" .

    Este método não é aplicável se pelo menos um dos players do roteador usar "NAT simétrico".
  • Depois que cada um dos jogadores souber seu endereço externo, ele deve, de alguma forma, dar seu endereço a todos os outros jogadores.

    Para que os jogadores possam trocar seus endereços, você pode usar seu servidor público. No diagrama, é chamado de servidor de endereço.


    Isso não requer necessariamente um servidor dedicado. Você pode usar qualquer hospedagem com um servidor web e com o suporte de qualquer linguagem de script, como PHP. Usando o protocolo HTTP, cada jogador deve enviar seu endereço externo para o servidor de endereços. E, em seguida, solicite os endereços de todos os outros jogadores.
  • Por exemplo, cada servidor de jogo pode registrar seu identificador exclusivo (ID do jogo) no servidor de endereço. Você pode usar qualquer sequência como o ID do jogo, por exemplo, o apelido (alias) do usuário do servidor. De alguma forma, o usuário do servidor informa o ID do jogo aos jogadores que ele deseja convidar para o jogo. E esses jogadores poderão ingressar no jogo solicitando, pelo servidor do endereço, os endereços externos de todos os participantes deste jogo.
  • Depois que cada jogador recebe os endereços externos de todos os outros jogadores, eles podem estabelecer conexões um para cada usando o método de perfuração UDP .

Source: https://habr.com/ru/post/pt485576/


All Articles