C# OpenVPN 상태 관찰자: NIC가 아니라 세션을 지켜보라
NetworkAvailabilityChanged는 VPN 이벤트가 아닙니다. 터널을 소유한 클라이언트의 상태 스트림을 읽고, 오래된 프로브를 경계하며, 연결마다 준비 상태를 게시하십시오.
터널이 죽는 동안에도 애플리케이션은 계속 동작합니다. Wi-Fi는 여전히 켜져 있고, 인터넷은 여전히 응답하며, 앱이 의존하는 프라이빗 API는 접근 불가능해졌습니다. 사람들이 여기서 흔히 기대는 .NET 이벤트인 NetworkAvailabilityChanged는 그 상황을 전혀 설명하지 않습니다. 그것은 머신의 집계 상태 변화를 보고하며, 그 상태는 VPN 연결 끊김과 재연결을 거쳐도 참으로 남을 수 있습니다. 관찰자는 터널을 소유한 클라이언트를 지켜봐야 하며, 네트워크 스택의 요약을 보면 안 됩니다.
가용성은 집계값이지 당신의 터널이 아니다
NetworkAvailabilityChanged 는 머신의 집계 가용성 응답이 바뀔 때 발생하며, 그 응답은 loopback이나 터널이 아닌 모든 활성 인터페이스를 셉니다.
Wi-Fi가 켜진 상태에서 VPN이 끊겨도 그것이 측정하는 것은 아무것도 바뀌지 않습니다. 그래서 당신이 신경 쓰는 바로 그 장애 동안 이벤트는 조용할 수 있습니다.
상태 스트림은 터널을 소유한 클라이언트의 것이다
어느 구성 요소가 연결을 소유하는지 식별하십시오. OpenVPN GUI나 서비스, OpenVPN Connect, 또는 애플리케이션이 시작하고 제어하는 프로세스입니다. 이들은 지원되는 인터페이스가 다른 서로 다른 소유자이며, 관찰자는 배포에 맞는 것을 사용해야 합니다. 통합이 허용된 OpenVPN 프로세스의 경우 관리 인터페이스가 현재 상태, 이력, 실시간 알림을 보고합니다. 현지화된 GUI 캡션에서 connected라는 단어를 찾는 대신 문서화된 구조화 메시지를 파싱하십시오.
관리 채널은 특권 제어 표면이므로 새 리스너를 열거나 GUI가 이미 가진 채널을 두고 다투지 마십시오.
세대 가드가 오래된 결과를 막는다
모든 관찰은 특정 연결 시도에 속합니다. 새 시도가 시작될 때, 채널이 끊길 때, 클라이언트가 종료를 보고할 때 세대 카운터를 증가시키십시오.
연결 A 중에 시작된 프로브를 생각해 봅시다. 터널이 끊기고 연결 B가 시작되며, 그 뒤에 A의 지연된 HTTP 성공이 도착합니다. 가드가 없으면 그 오래된 결과가 B가 아직 인증 중인데도 화면을 초록으로 만듭니다. 취소는 낭비되는 작업을 줄이고, 세대 검사가 오래된 완료가 현재 상태가 되는 것을 막습니다.
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
UI 디스패치 주변에도 같은 규칙을 적용하십시오. 마샬링된 업데이트는 세대가 지난 뒤에 도착할 수 있습니다.
초기화와 재연결은 하나의 수명 주기
첫 연결, 재연결, 채널 손실을 하나의 규칙 집합을 가진 하나의 수명 주기로 다루십시오.
- 인터페이스가 허용하는 곳에서는 현재 상태 읽기와 알림 구독을 한 단계로 하십시오.
- 모든 관찰에 연결 식별자와 세대를 태그하십시오.
- 재연결, 종료, 관찰 채널 손실 시 준비 상태를 무효화하십시오.
- 소유자가 초기화 완료를 보고한 뒤에만 프라이빗 리소스를 프로브하십시오.
- 현재 세대에 아직 속한 결과만 게시하십시오.
채널 손실은 연결 끊김이 아니라 알 수 없음을 뜻합니다. 알 수 없다고 보고하는 것이 증명할 수 없는 연결 끊김을 보고하는 것보다 정직하며, 재시도 로직이 추측으로 행동하지 못하게 합니다.
불리언이 아니라 증거를 보고하십시오
명명된 세션 상태와 리소스 준비 상태를 별도의 값으로 반환하고 각각 나이를 붙이십시오. 무언가를 결정해야 하는 소비자는 마지막 성공 프로브가 사십 초 전이라는 것을 볼 수 있으며, 이는 신선한 정보와 다른 정보입니다.
공인 IP 조회는 프라이빗 API가 필요한 경로를 사용했다는 증거가 아니므로 그런 것처럼 제시하지 마십시오.
강제는 관찰자 밖에 두라
콜백은 사후에 실행됩니다. 이벤트와 핸들러 사이에 머신을 떠난 패킷이 없다고 보장할 수 없습니다. 설계가 차단을 요구한다면 네트워크 정책이 그것을 제공해야 하며, 관찰자의 일은 호출자에게 증거를 설명하는 것입니다. 둘을 섞으면 불리언을 들고 있다는 이유로 자신이 방화벽이라고 믿는 코드가 생깁니다. 강제 계층은 지루하고 테스트 가능하게 유지하고, 관찰자는 읽는 사람으로 남게 하십시오.
그러면 UI 질문은 단순해집니다. 아는 것을 나이와 함께 보여 주고, 모를 때는 모른다고 말하십시오.