C# OpenVPN-tillståndsobservatör: bevaka sessionen, inte nätverkskortet
NetworkAvailabilityChanged är inte en VPN-händelse. Läs tillståndsströmmen från klienten som äger tunneln, skydda mot inaktuella prober och publicera beredskap per anslutning.
Din applikation fortsätter att fungera medan tunneln dör. Wi-Fi är fortfarande uppe, internet svarar fortfarande, och det privata API som appen är beroende av har blivit onåbart. Den .NET-händelse som folk vanligtvis griper efter här, NetworkAvailabilityChanged, beskriver inte den situationen alls. Den rapporterar en ändring i maskinens sammanlagda tillstånd, och det tillståndet kan förbli sant genom en VPN-frånkoppling och återanslutning. En observatör måste bevaka klienten som äger tunneln, inte nätverksstackens sammanfattning av den.
Tillgänglighet är ett aggregat, inte din tunnel
NetworkAvailabilityChanged utlöses när maskinens sammanlagda tillgänglighetssvar ändras, och det svaret räknar varje aktivt gränssnitt utom loopback eller en tunnel.
Ett VPN-avbrott medan Wi-Fi är uppe ändrar ingenting av det den mäter, därför kan händelsen förbli tyst genom precis det fel du bryr dig om.
Tillståndsströmmen tillhör klienten som äger tunneln
Ta reda på vilken komponent som äger anslutningen: OpenVPN GUI eller -tjänst, OpenVPN Connect, eller en process som din applikation startade och kontrollerar. Det är olika ägare med olika stödda gränssnitt, och observatören måste använda det som passar utrullningen. För en OpenVPN-process som du har tillstånd att integrera med rapporterar hanteringsgränssnittet aktuellt tillstånd, historik och realtidsaviseringar. Analysera de dokumenterade strukturerade meddelandena i stället för att söka efter ordet connected i en lokaliserad GUI-text.
Hanteringskanalen är en privilegierad kontrollsyta, så öppna inte en ny lyssnare och bråka inte med GUI:n om en kanal som den redan håller.
Generationsvakter stoppar inaktuella resultat
Varje observation tillhör ett specifikt anslutningsförsök. Öka en generationsräknare när ett nytt försök börjar, när kanalen tappas och när klienten rapporterar avslut.
Tänk på en probe som startade under anslutning A. Tunneln faller, anslutning B börjar, och därefter anländer A:s fördröjda HTTP-framgång. Utan en vakt gör det gamla resultatet skärmen grön medan B fortfarande autentiserar. Avbrytning minskar bortkastat arbete, och generationskontrollen är det som hindrar ett inaktuellt slutförande från att bli aktuellt tillstånd:
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
Tillämpa samma regel kring UI-utsändningen, eftersom den marshallade uppdateringen kan anlända efter att generationen har flyttat sig vidare.
Initiering och återanslutning är en livscykel
Behandla första anslutning, återanslutning och kanalförlust som en livscykel med en regeluppsättning:
- Läs aktuellt tillstånd och prenumerera på aviseringar i ett steg där gränssnittet tillåter det.
- Märk varje observation med anslutningens identitet och generationen.
- Ogiltigförklara beredskap vid återanslutning, avslut eller en förlorad observationskanal.
- Prova den privata resursen först efter att ägaren rapporterar att initieringen är klar.
- Publicera bara resultat som fortfarande tillhör den aktuella generationen.
En förlorad kanal betyder okänd, inte frånkopplad. Att rapportera okänd är ärligare än att rapportera en frånkoppling du inte kan bevisa, och det hindrar återförsökslogiken från att agera på en gissning.
Rapportera bevis, inte en boolean
Returnera det namngivna sessionstillståndet och resursberedskapen som separata värden, var och en med en ålder. En konsument som måste besluta något kan då se att den senaste lyckade proben är fyrtio sekunder gammal, vilket är annan information än en färsk.
En sökning av offentlig IP är inte bevis för att ett privat API använde den nödvändiga rutten, så presentera det inte som om det vore det.
Håll verkställandet utanför observatören
En callback körs i efterhand. Den kan inte garantera att ingen paket lämnade maskinen mellan händelsen och hanteraren. Om designen kräver blockering måste nätverkspolicyn leverera det, och observatörens uppgift är att förklara bevisen för sin anropare. Att blanda de två ger kod som tror att den är en brandvägg eftersom den håller en boolean. Håll verkställighetslagret tråkigt och testbart, och låt observatören förbli en läsare.
Då blir UI-frågan enkel: visa det som är känt, med dess ålder, och säg okänd när det är så.