Perdita di pacchetti ad alti bitrate
Andrea Murru | 19 Gennaio 2009In un progetto sul quale ho lavorato di recente, mi è capitato di avere a che fare con flussi (streaming multimediali) a bitrate relativamente alto, per l’hw in questione. Scrivo questo post perché siamo stati vittime di un nostro (banale) bug che ci è costato qualche giorno di test e qualche mal di testa: magari qualcuno potrà evitarseli leggendo questo post… non si trova molta documentazione in giro.
Intanto qualche altro dettaglio sul sistema: un client che riceve flussi multimediali in UDP (fino a 10 mbs) su windows embedded ce 6.0, realizzato in c++ con il visual studio 2005, utilizzando direttamente winsock2. Un thread si occupa della ricezione utilizzando semplicemente una socket in modalità bloccante in un ciclo di lettura che ha anche il compito di effetture alcune operazioni sui dati (poco onerose in termini di CPU) e di copiarli in un buffer dal quale un thread consumatore le preleva. Viene utilizzata la funzione recv(), visto che la modalità bloccante non è affatto un problema (e quindi l’ overlapped I/O è inutile) e le completion routine non sono ben supportate da windows embedded ce 6.0.
Tutto sembra funzionare bene, fino a bitrare inferiori a 2 mbs, ma superando tale valore… si manifestano degli strani problemi. Dopo molta fatica (visto che ovviamente non era possibile andare in debug, ma neppure scrivere su file se non pochi kbytes e quidi il debug stesso non poteva che avvenire anch’esso via rete), sembrava inequivocabile che si trattasse di perdite di pacchetti dallo 0.3% al 3% circa. A ridurre la nostra lucità di analisi si metteva anche il fatto che ad avere problema era solo uno streamer che utilizzavamo per la prima volta, mentre quello che avevamo utilizzato fino ad allora funzionava alla grande (ora sappiamo che dipendeva solo dal bitrate).
Il passo successivo (e molto poco divertente) è stato quello di usare wireshark per verificare se una tale perdita di pacchetti era in qualche modo imputabile alla nostra lettura… provate a cercare in un dump 1 paccheto perso, verificando che non ci siano buchi in un continuity counter a 4 bit e con il parser di wireshark bacato. Davvero poco divertente. Comunque le perdite non c’erano!
Il problema è semplicemente legato al fatto che il sistema operativo allocca un buffer interno, settato di defaut a pochi kbytes, che può facilmente venire saturato se il thread che effettua la lettura non è sufficientemente veloce o se viene sospeso (anche solo per pochi ms).
Fortunatamnte la soluzione esiste: basta settare un buffer di ricezione più grande.
unsigned bufferSize = 1024 * 1024;
::setsockopt(s_, SOL_SOCKET, SO_RCVBUF, (const char FAR*)&bufferSize, sizeof(bufferSize));
con 1 mbyte di buffer, a 10 mbs, si possono gestire circa 800 ms di flusso: si tratta di un valore congruo che ci ha permesso di eliminare del tutto le perdite.