La tua applicazione continua a funzionare mentre il tunnel muore. Il Wi-Fi è ancora attivo, Internet risponde ancora, e l'API privata da cui dipende l'app è diventata irraggiungibile. L'evento .NET a cui di solito si ricorre qui, NetworkAvailabilityChanged, non descrive affatto quella situazione. Segnala un cambiamento nella condizione aggregata della macchina, e quella condizione può restare vera attraverso una disconnessione e riconnessione VPN. Un osservatore deve sorvegliare il client che possiede il tunnel, non il riepilogo dello stack di rete.

La disponibilità è un aggregato, non il tuo tunnel

NetworkAvailabilityChanged si attiva quando cambia la risposta aggregata di disponibilità della macchina, e quella risposta conta qualsiasi interfaccia attiva tranne loopback o un tunnel.

Un calo della VPN con il Wi-Fi attivo non cambia nulla di ciò che misura, per questo l'evento può restare silenzioso proprio durante il guasto che ti interessa.

Il flusso di stato appartiene al client che possiede il tunnel

Identifica quale componente possiede la connessione: la GUI o il servizio OpenVPN, OpenVPN Connect, o un processo che la tua applicazione ha avviato e controlla. Sono proprietari diversi con interfacce supportate diverse, e l'osservatore deve usare quella che corrisponde al deployment. Per un processo OpenVPN con cui sei autorizzato a integrarti, l'interfaccia di gestione riporta lo stato corrente, una cronologia e notifiche in tempo reale. Analizza i messaggi strutturati documentati invece di cercare la parola connected in una didascalia della GUI localizzata.

Il canale di gestione è una superficie di controllo privilegiata, quindi non aprire un nuovo listener e non litigare con la GUI per un canale che già detiene.

Le protezioni di generazione fermano i risultati obsoleti

Ogni osservazione appartiene a un tentativo di connessione specifico. Incrementa un contatore di generazione quando inizia un nuovo tentativo, quando il canale si perde e quando il client segnala l'uscita.

Considera una sonda avviata durante la connessione A. Il tunnel cade, inizia la connessione B, e poi arriva il successo HTTP ritardato di A. Senza una protezione, quel vecchio risultato rende lo schermo verde mentre B si sta ancora autenticando. La cancellazione riduce il lavoro sprecato, e il controllo di generazione è ciò che impedisce a un completamento obsoleto di diventare stato corrente:

var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);

Applica la stessa regola attorno all'invio verso l'interfaccia, perché l'aggiornamento marshallato può arrivare dopo che la generazione è avanzata.

Inizializzazione e riconnessione sono un solo ciclo di vita

Tratta la prima connessione, la riconnessione e la perdita del canale come un solo ciclo di vita con un unico insieme di regole:

  • Leggi lo stato corrente e iscriviti alle notifiche in un solo passaggio dove l'interfaccia lo consente.
  • Etichetta ogni osservazione con l'identità della connessione e la generazione.
  • Invalida la disponibilità alla riconnessione, all'uscita o a un canale di osservazione perduto.
  • Sonda la risorsa privata solo dopo che il proprietario segnala l'inizializzazione completata.
  • Pubblica solo i risultati che appartengono ancora alla generazione corrente.

Un canale perduto significa sconosciuto, non disconnesso. Segnalare sconosciuto è più onesto che segnalare una disconnessione che non puoi dimostrare, e impedisce alla logica di nuovo tentativo di agire su una supposizione.

Riporta prove, non un booleano

Restituisci lo stato di sessione nominato e la disponibilità della risorsa come valori separati, ciascuno con un'età. Un consumatore che deve decidere qualcosa può allora vedere che l'ultima sonda riuscita ha quaranta secondi, che è un'informazione diversa da una fresca.

Una ricerca dell'IP pubblico non è prova che un'API privata abbia usato la rotta richiesta, quindi non presentarla come se lo fosse.

Tieni l'applicazione fuori dall'osservatore

Una callback viene eseguita a posteriori. Non può garantire che nessun pacchetto abbia lasciato la macchina tra l'evento e il gestore. Se il progetto richiede il blocco, la politica di rete deve fornirlo, e il compito dell'osservatore è spiegare le prove al chiamante. Mescolare le due cose produce codice che crede di essere un firewall perché tiene un booleano. Tieni il livello di applicazione noioso e testabile, e lascia che l'osservatore resti un lettore.

Allora la questione dell'interfaccia diventa semplice: mostra ciò che è noto, con la sua età, e dì sconosciuto quando lo è.

Articoli correlati