Observador de estado do OpenVPN em C#: vigie a sessão, não a placa de rede
NetworkAvailabilityChanged não é um evento de VPN. Leia o fluxo de estado do cliente proprietário, proteja-se de sondagens obsoletas e publique a prontidão por conexão.
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.