Tu aplicación sigue funcionando mientras el túnel muere. El Wi-Fi sigue activo, internet sigue respondiendo y la API privada de la que depende la aplicación se ha vuelto inalcanzable. El evento de .NET al que la gente suele recurrir aquí, NetworkAvailabilityChanged, no describe esa situación en absoluto. Informa de un cambio en la condición agregada de la máquina, y esa condición puede seguir siendo cierta durante una desconexión y reconexión de VPN. Un observador debe vigilar al cliente que posee el túnel, no el resumen de la pila de red.

La disponibilidad es un agregado, no tu túnel

NetworkAvailabilityChanged se dispara cuando cambia la respuesta agregada de disponibilidad de la máquina, y esa respuesta cuenta cualquier interfaz activa que no sea loopback ni un túnel.

Una caída de la VPN con el Wi-Fi activo no cambia nada de lo que mide, por eso el evento puede permanecer silencioso justo durante el fallo que te importa.

El flujo de estado pertenece al cliente que posee el túnel

Identifica qué componente posee la conexión: la GUI o el servicio de OpenVPN, OpenVPN Connect, o un proceso que tu aplicación inició y controla. Son propietarios distintos con interfaces compatibles distintas, y el observador debe usar la que corresponda al despliegue. Para un proceso de OpenVPN con el que tienes autorización para integrarte, la interfaz de gestión informa del estado actual, un historial y notificaciones en tiempo real. Analiza los mensajes estructurados documentados en lugar de buscar la palabra connected en un rótulo de GUI localizado.

El canal de gestión es una superficie de control privilegiada, así que no abras un nuevo escucha ni discutas con la GUI por un canal que ya tiene.

Las protecciones de generación detienen los resultados obsoletos

Cada observación pertenece a un intento de conexión concreto. Incrementa un contador de generación cuando empieza un intento nuevo, cuando se pierde el canal y cuando el cliente informa de la salida.

Considera una sonda iniciada durante la conexión A. El túnel cae, empieza la conexión B y después llega el éxito HTTP retrasado de A. Sin una protección, ese resultado antiguo pone la pantalla en verde mientras B todavía se autentica. La cancelación reduce el trabajo desperdiciado, y la comprobación de generación es lo que impide que una finalización obsoleta se convierta en estado actual:

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

Aplica la misma regla alrededor del envío a la UI, porque la actualización empaquetada puede llegar después de que la generación haya avanzado.

La inicialización y la reconexión son un solo ciclo de vida

Trata la primera conexión, la reconexión y la pérdida de canal como un solo ciclo de vida con un único conjunto de reglas:

  • Lee el estado actual y suscríbete a las notificaciones en un solo paso donde la interfaz lo permita.
  • Etiqueta cada observación con la identidad de la conexión y la generación.
  • Invalida la disponibilidad en la reconexión, la salida o un canal de observación perdido.
  • Sondea el recurso privado solo después de que el propietario informe de que la inicialización está completa.
  • Publica solo los resultados que todavía pertenecen a la generación actual.

Un canal perdido significa desconocido, no desconectado. Informar de desconocido es más honesto que informar de una desconexión que no puedes demostrar, y evita que la lógica de reintentos actúe sobre una suposición.

Informa de evidencia, no de un booleano

Devuelve el estado de sesión nombrado y la disponibilidad del recurso como valores separados, cada uno con una edad. Un consumidor que tiene que decidir algo puede entonces ver que la última sonda exitosa tiene cuarenta segundos, lo que es información distinta de una reciente.

Una consulta de IP pública no es evidencia de que una API privada usara la ruta requerida, así que no la presentes como si lo fuera.

Mantén la aplicación fuera del observador

Una devolución de llamada se ejecuta después del hecho. No puede garantizar que ningún paquete haya salido de la máquina entre el evento y el manejador. Si el diseño requiere bloqueo, la política de red debe proporcionarlo, y la tarea del observador es explicar la evidencia a su llamador. Mezclar ambas cosas produce código que cree ser un cortafuegos porque tiene un booleano. Mantén la capa de aplicación aburrida y comprobable, y deja que el observador siga siendo un lector.

Entonces la cuestión de la UI se vuelve simple: muestra lo que se sabe, con su edad, y di desconocido cuando lo sea.

Artículos relacionados