A sua aplicação continua a funcionar enquanto o túnel morre. O Wi-Fi continua ativo, a internet continua a responder e a API privada de que a aplicação depende tornou-se inalcançável. O evento .NET a que as pessoas costumam recorrer aqui, NetworkAvailabilityChanged, não descreve essa situação de forma alguma. Ele relata uma mudança na condição agregada da máquina, e essa condição pode permanecer verdadeira durante uma desconexão e reconexão de VPN. Um observador tem de vigiar o cliente que possui o túnel, não o resumo da pilha de rede.

A disponibilidade é um agregado, não o seu túnel

NetworkAvailabilityChanged é acionado quando a resposta agregada de disponibilidade da máquina muda, e essa resposta conta qualquer interface ativa exceto loopback ou um túnel.

Uma queda de VPN com o Wi-Fi ativo não muda nada do que ele mede, por isso o evento pode ficar silencioso exatamente durante a falha que lhe importa.

O fluxo de estado pertence ao cliente que possui o túnel

Identifique qual componente possui a conexão: a GUI ou o serviço do OpenVPN, o OpenVPN Connect, ou um processo que a sua aplicação iniciou e controla. São proprietários diferentes com interfaces suportadas diferentes, e o observador tem de usar a que corresponde à implementação. Para um processo OpenVPN com o qual está autorizado a integrar-se, a interface de gestão relata o estado atual, um histórico e notificações em tempo real. Analise as mensagens estruturadas documentadas em vez de procurar a palavra connected numa legenda de GUI localizada.

O canal de gestão é uma superfície de controlo privilegiada, por isso não abra um novo listener nem discuta com a GUI por um canal que ela já detém.

As proteções de geração travam os resultados obsoletos

Cada observação pertence a uma tentativa de conexão específica. Incremente um contador de geração quando começa uma nova tentativa, quando o canal se perde e quando o cliente relata a saída.

Considere uma sondagem iniciada durante a conexão A. O túnel cai, a conexão B começa e depois chega o sucesso HTTP atrasado de A. Sem uma proteção, esse resultado antigo deixa o ecrã verde enquanto B ainda se autentica. O cancelamento reduz o trabalho desperdiçado, e a verificação de geração é o que impede uma conclusão obsoleta de se tornar o estado atual:

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

Aplique a mesma regra em torno do envio para a UI, porque a atualização marshalled pode chegar depois de a geração ter avançado.

A inicialização e a reconexão são um único ciclo de vida

Trate a primeira conexão, a reconexão e a perda de canal como um único ciclo de vida com um único conjunto de regras:

  • Leia o estado atual e subscreva as notificações num único passo onde a interface o permitir.
  • Etiquete cada observação com a identidade da conexão e a geração.
  • Invalide a prontidão na reconexão, na saída ou num canal de observação perdido.
  • Sonde o recurso privado apenas depois de o proprietário relatar a inicialização concluída.
  • Publique apenas os resultados que ainda pertencem à geração atual.

Um canal perdido significa desconhecido, não desligado. Relatar desconhecido é mais honesto do que relatar uma desconexão que não pode provar, e impede a lógica de nova tentativa de agir com base numa suposição.

Relate evidência, não um booleano

Devolva o estado de sessão nomeado e a prontidão do recurso como valores separados, cada um com uma idade. Um consumidor que tenha de decidir algo pode então ver que a última sondagem bem-sucedida tem quarenta segundos, o que é informação diferente de uma recente.

Uma consulta de IP público não é evidência de que uma API privada usou a rota exigida, por isso não a apresente como se fosse.

Mantenha a aplicação fora do observador

Um callback executa-se depois do facto. Não pode garantir que nenhum pacote saiu da máquina entre o evento e o manipulador. Se o desenho exigir bloqueio, a política de rede tem de o fornecer, e a tarefa do observador é explicar a evidência ao seu chamador. Misturar os dois produz código que acredita ser uma firewall porque guarda um booleano. Mantenha a camada de aplicação aborrecida e testável, e deixe o observador continuar a ser um leitor.

Então a questão da UI torna-se simples: mostre o que se sabe, com a sua idade, e diga desconhecido quando o for.

Artigos relacionados