UDP non bloccante o un thread separato per la ricezione?


10

Sto creando un gioco multiplayer (per meno di 64 giocatori). Ho già deciso di avere un thread separato per il loop di rete, ma mi chiedevo se sarebbe meglio creare un thread aggiuntivo per ricevere UDP o impostare il socket di ricezione su non-blocking (senza thread aggiuntivo).

O è meglio usare un altro metodo come Asynchronous Sockets? I metodi migliori sono sempre i benvenuti!

Risposte:


6

Va bene, prima di tutto, se hai qualcosa e funziona, di solito è una buona idea lasciarlo così. Perché riparare ciò che non è rotto?

Ma se hai problemi e vuoi davvero riscrivere il tuo codice di rete, penso che tu abbia quattro scelte principali:

  1. Codice di blocco multithread (cosa stai facendo in questo momento)
  2. Prese senza blocco con notifica attivata dal livello
  3. Prese non bloccanti con notifica di modifica della disponibilità
  4. Zoccoli asincroni

Avendo scritto molti client e server multiplayer (dal peer-to-peer al multiplayer di massa), mi piace pensare che l'opzione 2 porti alla minima complessità, con prestazioni piuttosto buone, sia per le parti del gioco che per quelle del server e del client. Come secondo vicino, vorrei andare con l'opzione 4, ma questo di solito richiede di ripensare l'intero programma e lo trovo principalmente utile per i server e non per i client.

In particolare, vorrei sconsigliare il blocco dei socket in un ambiente multithread, poiché in genere ciò comporta il blocco e altre funzionalità di sincronizzazione che non solo aumentano notevolmente la complessità del codice, ma possono anche degradarne le prestazioni, poiché alcuni thread attendono altri.

Ma soprattutto, la maggior parte delle implementazioni socket (e la maggior parte delle implementazioni I / O in questo) sono non bloccanti al livello più basso. Le operazioni di blocco sono semplicemente fornite per semplificare lo sviluppo di programmi banali. Quando un socket sta bloccando, la CPU in quel thread è completamente inattiva, quindi perché creare un'astrazione non bloccante sull'astrazione bloccante su un'attività già non bloccante?

La programmazione di socket non bloccanti è un po 'scoraggiante se non l'hai provata, ma risulta abbastanza semplice e se stai già eseguendo il polling di input, hai già la mentalità di fare socket non bloccanti.

La prima cosa che vuoi fare è impostare il socket su non-blocking. Lo fai con fcntl().

Dopo di che, prima di farlo send(), recv(), sendto(), recvfrom(), accept()( connect()è un po 'diverso), o altre chiamate che potrebbero bloccare il filo, si chiama select()sul socket. select()indica se è possibile eseguire o meno un'operazione di lettura o scrittura successiva sul socket senza bloccarla. In tal caso, puoi tranquillamente eseguire l'operazione desiderata e il socket non si bloccherà.

Includere questo in un gioco è abbastanza semplice. Se hai già un loop di gioco, ad esempio in questo modo:

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

potresti cambiarlo in questo modo:

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

dove

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

In termini di risorse, l'unico libro che penso che tutti debbano avere nei loro scaffali, anche se è l'unico libro che hanno, è " Unix Network Programming, Vol 1. " del compianto Richard Stevens. Non importa se si esegue Windows o altri sistemi operativi o la programmazione socket lingua. Non pensare di aver compreso gli zoccoli finché non hai letto questo libro.

Un'altra pagina in cui è possibile trovare una panoramica generale delle soluzioni disponibili in termini di programmazione multipla socket (principalmente rilevante per la programmazione server) è questa pagina .


Ho appena avviato il mio motore di gioco, quindi la riscrittura non è un problema. Questa risposta è stata davvero utile e grazie per la raccomandazione sul libro. Conosci anche un libro più specifico sulla rete nei giochi?
Yannick Lange,

3
@YannickLange: No, ma non consiglierei di cercare libri specifici per il gioco, dal momento che la rete è praticamente indipendente dal genere, e anche nessun altro libro è all'altezza del libro di Stevens. Complimenti per l'utilizzo di UDP, poiché l'utilizzo di un protocollo orientato ai messaggi è una buona idea quando si scrivono programmi orientati ai messaggi (come la maggior parte dei giochi). Molte persone tendono a finire per scrivere un'astrazione di messaggi su TCP, che è di per sé un'astrazione orientata al flusso costruita su un protocollo orientato ai messaggi (IP).
Panda Pajama,

-1

Non hai scritto quale linguaggio di programmazione stai usando, ma la maggior parte dei linguaggi fornisce buoni framework di socket asincroni che spesso utilizzano funzionalità del sistema operativo sottostante.

Quando scrivi la tua implementazione thread basata su socket bloccanti (ricorda: quando hai più client, hai bisogno di un thread per ogni singolo socket), reinventerai ciò che già fornisce il framework di socket asincrono.

Quindi ti consiglio di usare socket asincroni.


4
perché ha bisogno di un thread per ogni singolo socket? Soprattutto con prese asincrone? Molti server pesanti utilizzano un modello di "pool di thread" per elaborare i dati dei socket, dove ogni thread serve N client e vengono generati o uccisi secondo necessità. Basti pensare che questa risposta necessita di un miglioramento: D
Grimshaw,

@Grimshaw Penso che tu abbia frainteso la mia risposta. Penso di aver chiarito che un modello multi-thread sarebbe necessario quando si utilizzano socket bloccanti.
Philipp,

@Philipp Non ho detto quale linguaggio di programmazione uso perché, ho pensato che non fosse molto rilevante per la domanda (ad ogni modo uso c ++). Vi ringrazio per la vostra raccomandazione di utilizzare socket asincrono. Conosci un libro / una pagina web consigliati per l'apprendimento di questi tipi di metodi nel gioco (motori)?
Yannick Lange,

@Grimshaw: E in che modo ogni thread serve i Nclient? Se stai bloccando e vuoi assicurarti che ogni socket ottenga i suoi dati elaborati indipendentemente dallo stato del resto dei socket, devi avere un thread per socket. Quello o non usare prese di blocco.
Panda Pajama,

Il mio commento è stato alla prima versione della tua risposta, nel frattempo l'hai modificata e corretta, dimentica quello che ho detto: D
Grimshaw,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.