Affidabilità del riconoscimento tramite UDP


16

Ho una domanda su UDP. Per il contesto, sto lavorando a un gioco d'azione in tempo reale.

Ho letto un po 'delle differenze tra UDP e TCP e sento di capirle abbastanza bene, ma c'è un pezzo che non si è mai sentito corretto, che è affidabilità e riconoscimenti specifici . Comprendo che UDP non offre affidabilità per impostazione predefinita (ovvero i pacchetti possono essere eliminati o arrivare fuori servizio). Quando è richiesta una certa affidabilità, la soluzione che ho visto (che ha senso concettualmente) è utilizzare i riconoscimenti (ovvero il server invia un pacchetto al client e quando il client riceve quel messaggio, invia un riconoscimento al server) .

Cosa succede quando viene rilasciato il riconoscimento?

Nell'esempio sopra (un server che invia un pacchetto a un client), il server gestisce la potenziale perdita di pacchetti rinviando i pacchetti ogni frame fino a quando non vengono ricevuti riconoscimenti per quei pacchetti. Potresti ancora imbatterti in problemi di larghezza di banda o messaggi fuori servizio, ma puramente dal punto di vista della perdita di pacchetti, il server è coperto.

Tuttavia, se il client invia un riconoscimento che non arriva mai, il server non avrebbe altra scelta che interrompere infine l'invio di quel messaggio, che potrebbe interrompere il gioco se fossero necessarie le informazioni contenute in quel pacchetto. Potresti adottare un approccio simile al server (ovvero continuare a inviare riconoscimenti fino a quando non ricevi un ack per l'ack?), Ma quell'approccio ti farebbe andare avanti e indietro per sempre (dal momento che avresti bisogno di un ack per il ack per il ack) e così via).

Sento che la mia logica di base è corretta qui, il che mi lascia con due opzioni.

  1. Invia un singolo pacchetto di riconoscimento e spera per il meglio.
  2. Invia una manciata di pacchetti di riconoscimento (forse 3-4) e spera per il meglio, supponendo che non tutti verranno eliminati.

C'è una risposta a questo problema? Sto fondamentalmente fraintendendo qualcosa? C'è qualche garanzia sull'uso di UDP di cui non sono a conoscenza? Mi sento titubante ad andare avanti con troppo codice di rete finché non mi sento a mio agio che la mia logica è solida.


11
Forse ti manca un'idea di "timeout" e "tentativi".
Kromster dice di sostenere Monica il

Potrei esserlo, certo. Stai suggerendo che la mia logica è corretta e, per non sembrare troppo negativa, ma durante la programmazione di rete, non posso assumere alcuna garanzia su praticamente qualsiasi informazione di rete? Nel corso di un gioco in tempo reale, ci sono un sacco di informazioni potenzialmente eliminate, il che va bene, ma voglio solo assicurarmi di aver capito il problema.
Grimelios,

10
Nessuna garanzia. Giusto. Non includere mai "speranza" nei tuoi algoritmi. Devono gestire QUALSIASI combinazione sfortunata. PS Abbiamo semplicemente passato a TCP nel nostro RTS, dove si occupa di eberything, poiché abbiamo bisogno di una comunicazione affidabile (per la simulazione lockstep).
Kromster dice di sostenere Monica il

5
Usa TCP quando è necessaria l'affidabilità, usa UDP quando non ha importanza. Ad esempio, le coordinate del giocatore vengono inviate nel mio gioco tramite UDP. Uso l'interpolazione e il livellamento per smussare eventuali pacchetti mancanti. funziona come un fascino. cose che hanno davvero bisogno di essere affidabili ma possono essere un po 'più lente vengono inviate via TCP. Se hai uno stato in cui uno stato più nuovo invalida il vecchio stato, UDP è una buona scelta perché non importa quando qualcosa nel mezzo è stato lasciato cadere 8e.g. Posizione del giocatore).
Polygnome,

Questa non è una risposta diretta alla tua domanda, ma ti consiglio vivamente di richiedere un riconoscimento in un gioco in tempo reale solo quando sono assolutamente necessari (ad esempio, alla connessione iniziale). È molto più semplice (e robusto) progettare sia il client che il server in modo che "lavorino con quello che hanno" fino a quando non riescono a ottenere un nuovo pacchetto in un sistema senza stato. Quake 3 lo ha fatto incredibilmente bene con un sistema basato su istantanee . Anche le biblioteche come Enet possono inviare solo determinati pacchetti in modo affidabile, per quei casi in cui ne hai davvero bisogno
jrh

Risposte:


32

Questa è una forma del problema dei due generali e hai ragione: nessun numero di tentativi è sufficiente per garantire perfettamente la ricezione.

In pratica nei giochi, di solito c'è un orizzonte temporale oltre il quale le informazioni non contano davvero, anche se tecnicamente arrivano in modo affidabile. Come scoprire che hai avuto un colpo alla testa perfetto in fila 2 secondi fa - è troppo tardi perché il giocatore possa usare queste informazioni ora.

Se la tua perdita di pacchetti è così alta che non riesci a ottenere regolarmente le informazioni necessarie all'interno di una finestra di reazione ristretta, allora per una partita in tempo reale potresti essere meglio calciare il giocatore e cercare di trovare una corrispondenza migliore per loro altrove, piuttosto che continuare a provare a inviare il pacchetto per emulare una connessione affidabile.

Per questo motivo, alcuni sistemi di replica dei giochi saltano il riconoscimento e riprovano del tutto e optano per lo spam appena il più recente aggiornamento possibile. Se uno viene lasciato cadere o arriva in ritardo, peccato, saltalo, raccogli il successivo e continua, facendo affidamento sui sistemi di previsione e interpolazione per appianare il divario e ridurre al minimo il singhiozzo visibile al giocatore.

Improvvisamente voglio iniziare a chiamare questa "replica Simba" per come ignora i problemi nel passato e cerca di vivere nel momento presente. ;)

Rafiki laying down some reductio ad absurdum on that life philosophy

Una soluzione ibrida è quella di correre avanti inviando il nuovo aggiornamento E (poiché gli aggiornamenti dello stato del gioco possono essere spesso piuttosto piccoli / comprimibili ) comprimono anche l'ultimo aggiornamento, e forse quello precedente ... Quindi nel caso in cui il client li avesse persi , non devi aspettare un tempo di andata e ritorno completo per scoprirlo e risolverlo. La maggior parte delle volte il client lo ha già visto, quindi in questo modo ci sono dati ridondanti, ma la latenza per correggere un messaggio perso è inferiore. Gli aggiornamenti del client possono includere il numero di indice dell'aggiornamento consecutivo più recente che hanno visto, quindi puoi essere minimamente conservativo con quanti vecchi aggiornamenti includi nel pacchetto di aggiornamento successivo.

Potresti anche implementare un sistema a due livelli come un altro tipo di ibrido, in cui lo stato di breve durata viene replicato in modo inaffidabile a fuoco rapido e lo stato a lungo termine viene sincronizzato in modo affidabile, utilizzando TCP o l'implementazione della tua affidabilità con un nuovo tentativo contare. Questo diventa più complesso da gestire, perché hai due sistemi di messaggistica da mantenere e le due istantanee possono non essere sincronizzate tra loro, aggiungendo una nuova classe di edge case.


1
+1, ben scritto. Vorrei solo sottolineare che questo è più rilevante per i giochi d'azione / in tempo reale. I giochi TBS e RTS (e alcuni eventi di giochi d'azione) hanno una visione diversa su "orizzonte temporale oltre il quale le informazioni non contano davvero".
Kromster dice di sostenere Monica il

3
Sì, per un gioco a turni, immagino che uno userebbe TCP piuttosto che provare a far rotolare il proprio livello di affidabilità sopra UDP. ;) Classificherei comunque il micro in un RTS come il tipo di gameplay con un orizzonte temporale esatto però - quell'approccio ibrido potrebbe fare bene lì, dove hai sia aggiornamenti a bassa latenza per il calore del momento che un rete di sicurezza per la gestione retroattiva di eventi critici persi come la spesa di risorse.
DMGregory

È estremamente utile e in qualche modo convalida la mia preoccupazione iniziale. Grazie mille.
Grimelios,

2
Potrebbe anche essere utile menzionare la correzione degli errori in avanti. Progetta il tuo protocollo in modo tale che il ricevitore possa capire in modo indipendente che un pacchetto è stato eliminato alla ricezione del pacchetto successivo, aggiungendo alcuni dati extra per appianare l'interpolazione richiesta. Questo può essere utile perché spesso i pacchetti UDP non sono comunque pieni e si inviano pacchetti più piccoli più spesso per mantenere bassa la latenza. L'aggiunta di alcuni byte extra non danneggerà la latenza e la larghezza di banda non è un problema in questi casi.
Salterio

@MSalters Direi che vale la pena approfondire nella sua risposta, se sei all'altezza. Lo voterei. :)
DMGregory

9

L'approccio utilizzato da TCP è che il mittente continuerà a inviare nuovamente il pacchetto fino a quando non riceve un riconoscimento. Il destinatario ignorerà i pacchetti duplicati, ma invierà comunque dei riconoscimenti per essi. Il mittente ignorerà i doppi riconoscimenti.

Se un pacchetto viene perso, il mittente lo rinvia, come già sapete.
Se un riconoscimento viene perso, il mittente rinvia il pacchetto originale, facendo sì che il destinatario rinvii il riconoscimento.

Se un riconoscimento non viene ricevuto entro un certo tempo (forse 60 secondi o 20 tentativi), il giocatore viene considerato disconnesso dal gioco. È necessario implementare una sorta di regola di timeout, altrimenti un giocatore che scollega il cavo di rete legherà per sempre le risorse sul server.


Una caratteristica essenziale di TCP è che il mittente non ha bisogno di preoccuparsi se un determinato pacchetto sia stato riconosciuto, ma deve soprattutto preoccuparsi del "segno di alta marea" e per quanto tempo i pacchetti sono stati eccezionali senza che il segno di alta marea si muovesse.
supercat

1
@supercat Non direi che è essenziale; più come un'ottimizzazione.
user253751

Per quanto riguarda la cosa tra parentesi (invio di ACK per i pacchetti che hai già ricevuto), penso che dovresti effettivamente enfatizzarla invece di parentesi. Sembra mancare dalla comprensione del PO (o almeno dalla sua descrizione).
Angew non è più orgoglioso di SO

@Angew fatto ora.
user253751

6

Se vuoi reinventare TCP, ha senso guardare TCP prima , che tratta esattamente il problema descritto (parte della soluzione consiste nell'utilizzare i valori definiti dall'utente per tentativi e timeout).

Le soluzioni che utilizzano 2 canali, un canale TCP (per comunicazioni affidabili) e un canale UDP (per comunicazioni a bassa latenza) non sono rare.

Alcune soluzioni rilevano quando un client manca di informazioni per troppo tempo e avviano una risincronizzazione, che può utilizzare UDP o TCP.

Un altro approccio comune è quello di progettare la comunicazione in modo tale che non si basi affatto sui riconoscimenti, ma questo è al di fuori dell'ambito della domanda.


3

In un RTS non puoi davvero usare un protocollo come TCP e non puoi rendere affidabile UDP. Se ci provi, il gioco si bloccherà ogni volta che si verifica un rallentamento della rete.

Invece, si progetta il protocollo in modo che i pacchetti persi non contino troppo.

La versione breve è che non ti interessa dove sono stati gli altri giocatori nell'ultimo fotogramma fintanto che sai dove si trovano ora . La versione lunga è più complicata.

La domanda diventa allora: cosa fai quando un pacchetto scompare? E la risposta è ... indovina. Il giocatore si sta probabilmente muovendo in linea retta, giusto? Spostali di un altro passo lungo quella linea. ... Tranne il fatto che nessun giocatore RTS si muove mai in linea retta. E poi c'è il rilevamento delle collisioni.

Questo è difficile. Molti giochi sbagliano. Si può sostenere che non esiste un diritto risposta a questo, solo vari errori che possono essere scambiati.

Il motivo per cui questi giochi funzionano abbastanza bene non è solo che hanno riflettuto a lungo su questi problemi, ma anche che Internet è diventato abbastanza affidabile. Quasi tutti i pacchetti UDP raggiungono la loro destinazione in modo tempestivo. (A meno che non ci sia un problema permanente come un firewall)


Warcraft 3 utilizza TCP.
fsp,
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.