C#-OpenVPN-Zustandsbeobachter: die Sitzung überwachen, nicht die Netzwerkkarte
NetworkAvailabilityChanged ist kein VPN-Ereignis. Lesen Sie den Zustandsstrom des besitzenden Clients, schützen Sie sich vor veralteten Prüfungen und melden Sie Bereitschaft je Verbindung.
Ihre Anwendung läuft weiter, während der Tunnel stirbt. WLAN ist noch aktiv, das Internet antwortet noch, und die private API, von der die App abhängt, ist unerreichbar geworden. Das .NET-Ereignis, zu dem man hier üblicherweise greift, NetworkAvailabilityChanged, beschreibt diese Situation überhaupt nicht. Es meldet eine Änderung des aggregierten Zustands der Maschine, und dieser Zustand kann über eine VPN-Trennung und -Wiederverbindung hinweg wahr bleiben. Ein Beobachter muss den Client überwachen, dem der Tunnel gehört, nicht die Zusammenfassung des Netzwerk-Stacks.
Verfügbarkeit ist ein Aggregat, nicht Ihr Tunnel
NetworkAvailabilityChanged wird ausgelöst, wenn sich die aggregierte Verfügbarkeitsantwort der Maschine ändert, und diese Antwort zählt jede aktive Schnittstelle außer Loopback oder einem Tunnel.
Ein VPN-Abbruch bei aktivem WLAN ändert nichts von dem, was sie misst, deshalb kann das Ereignis genau während der Störung stumm bleiben, die Sie betrifft.
Der Zustandsstrom gehört dem Client, dem der Tunnel gehört
Bestimmen Sie, welche Komponente die Verbindung besitzt: die OpenVPN-GUI oder der Dienst, OpenVPN Connect oder ein Prozess, den Ihre Anwendung gestartet hat und steuert. Das sind unterschiedliche Eigentümer mit unterschiedlichen unterstützten Schnittstellen, und der Beobachter muss die zum Deployment passende verwenden. Bei einem OpenVPN-Prozess, mit dem Sie sich integrieren dürfen, meldet die Management-Schnittstelle den aktuellen Zustand, eine Historie und Echtzeitbenachrichtigungen. Analysieren Sie die dokumentierten strukturierten Nachrichten, statt in einer lokalisierten GUI-Beschriftung nach dem Wort connected zu suchen.
Der Management-Kanal ist eine privilegierte Steuerfläche, also öffnen Sie keinen neuen Listener und streiten Sie nicht mit der GUI um einen Kanal, den sie bereits hält.
Generationswächter stoppen veraltete Ergebnisse
Jede Beobachtung gehört zu einem bestimmten Verbindungsversuch. Erhöhen Sie einen Generationszähler, wenn ein neuer Versuch beginnt, wenn der Kanal verloren geht und wenn der Client einen Exit meldet.
Betrachten Sie eine Prüfung, die während Verbindung A gestartet wurde. Der Tunnel fällt, Verbindung B beginnt, und danach trifft der verzögerte HTTP-Erfolg von A ein. Ohne Wächter macht dieses alte Ergebnis die Anzeige grün, während B noch authentifiziert. Abbruch reduziert verschwendete Arbeit, und die Generationsprüfung verhindert, dass ein veralteter Abschluss zum aktuellen Zustand wird:
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
Wenden Sie dieselbe Regel um den UI-Versand an, denn das gemarshallte Update kann eintreffen, nachdem die Generation weitergelaufen ist.
Initialisierung und Wiederverbindung sind ein Lebenszyklus
Behandeln Sie Erstverbindung, Wiederverbindung und Kanalverlust als einen Lebenszyklus mit einem Regelsatz:
- Lesen Sie den aktuellen Zustand und abonnieren Sie Benachrichtigungen in einem Schritt, wo die Schnittstelle es erlaubt.
- Versehen Sie jede Beobachtung mit der Verbindungsidentität und der Generation.
- Machen Sie die Bereitschaft bei Wiederverbindung, Exit oder verlorenem Beobachtungskanal ungültig.
- Prüfen Sie die private Ressource erst, nachdem der Eigentümer den Abschluss der Initialisierung meldet.
- Veröffentlichen Sie nur Ergebnisse, die noch zur aktuellen Generation gehören.
Ein verlorener Kanal bedeutet unbekannt, nicht getrennt. Unbekannt zu melden ist ehrlicher, als eine Trennung zu melden, die Sie nicht beweisen können, und es hindert die Wiederholungslogik daran, auf einer Vermutung zu handeln.
Melden Sie Belege, keinen Boolean
Geben Sie den benannten Sitzungszustand und die Ressourcenbereitschaft als getrennte Werte zurück, jeder mit einem Alter. Ein Verbraucher, der etwas entscheiden muss, sieht dann, dass die letzte erfolgreiche Prüfung vierzig Sekunden alt ist, was eine andere Information ist als eine frische.
Eine öffentliche IP-Abfrage ist kein Beleg dafür, dass eine private API die erforderliche Route genutzt hat, also stellen Sie sie nicht so dar.
Halten Sie die Durchsetzung außerhalb des Beobachters
Ein Callback läuft im Nachhinein. Er kann nicht garantieren, dass zwischen Ereignis und Handler kein Paket die Maschine verlassen hat. Wenn das Design Blockierung erfordert, muss die Netzwerkrichtlinie sie liefern, und die Aufgabe des Beobachters ist es, dem Aufrufer die Belege zu erklären. Beides zu vermischen erzeugt Code, der sich für eine Firewall hält, weil er einen Boolean besitzt. Halten Sie die Durchsetzungsschicht langweilig und testbar, und lassen Sie den Beobachter ein Leser bleiben.
Dann wird die UI-Frage einfach: Zeigen Sie, was bekannt ist, mit seinem Alter, und sagen Sie unbekannt, wenn es so ist.