Назіральнік стану OpenVPN на C#: сачыце за сеансам, а не за сеткавай картай
NetworkAvailabilityChanged — гэта не падзея VPN. Чытайце струмень стану ад кліента-уладальніка, абараняйцеся ад састарэлых праверак і публікуйце гатоўнасць для кожнага злучэння.
Ваша праграма працягвае працаваць, пакуль тунель памірае. Wi-Fi яшчэ ўключаны, інтэрнэт яшчэ адказвае, а прыватны API, ад якога залежыць праграма, стаў недасяжным. Падзея .NET, да якой звычайна звяртаюцца ў гэтым выпадку, NetworkAvailabilityChanged, зусім не апісвае гэтую сітуацыю. Яна паведамляе пра змену сукупнага стану машыны, і гэты стан можа заставацца сапраўдным праз адключэнне і паўторнае падключэнне VPN. Назіральнік павінен сачыць за кліентам, які валодае тунелем, а не за зводкай сеткавага стэка пра яго.
Даступнасць — гэта сукупнае значэнне, а не ваш тунель
NetworkAvailabilityChanged спрацоўвае, калі змяняецца сукупны адказ машыны пра даступнасць, і гэты адказ улічвае любы падняты інтэрфейс, акрамя loopback або тунеля.
Падзенне VPN пры ўключаным Wi-Fi не змяняе нічога з таго, што яна вымярае, таму падзея можа маўчаць падчас менавіта той збою, які вас хвалюе.
Струмень стану належыць кліенту, які валодае тунелем
Вызначце, які кампанент валодае злучэннем: OpenVPN GUI або служба, OpenVPN Connect або працэс, які запусціла і кантралюе ваша праграма. Гэта розныя ўладальнікі з рознымі падтрыманымі інтэрфейсамі, і назіральнік павінен выкарыстоўваць той, што адпавядае разгортванню. Для працэсу OpenVPN, з якім вам дазволена інтэгравацца, інтэрфейс кіравання паведамляе бягучы стан, гісторыю і апавяшчэнні ў рэальным часе. Разбірайце дакументаваныя структураваныя паведамленні замест пошуку ў лакалізаваным загалоўку GUI слова connected.
Канал кіравання — гэта прывілеяваная паверхня кіравання, таму не адкрывайце новы слухач і не змагайцеся з GUI за канал, якім ён ужо валодае.
Ахоўнікі пакаленняў спыняюць састарэлыя вынікі
Кожнае назіранне належыць пэўнай спробе злучэння. Павялічвайце лічыльнік пакалення, калі пачынаецца новая спроба, калі губляецца канал і калі кліент паведамляе пра выхад.
Разгледзім праверку, пачатую падчас злучэння A. Тунель падае, пачынаецца злучэнне B, і потым прыходзіць запазнены поспех HTTP ад A. Без ахоўніка стары вынік робіць экран зялёным, пакуль B яшчэ аўтэнтыфікуецца. Скасаванне паменшвае марную працу, а праверка пакалення не дазваляе састарэламу завяршэнню стаць бягучым станам:
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
Прымяняйце тое ж правіла вакол адпраўкі UI, бо перададзенае абнаўленне можа прыйсці пасля таго, як пакаленне рушыла далей.
Ініцыялізацыя і паўторнае падключэнне — адзін жыццёвы цыкл
Разглядайце першае падключэнне, паўторнае падключэнне і страту канала як адзін жыццёвы цыкл з адным наборам правілаў:
- Чытайце бягучы стан і падпісвайцеся на апавяшчэнні за адзін крок, дзе гэта дазваляе інтэрфейс.
- Пазначайце кожнае назіранне ідэнтычнасцю злучэння і пакаленнем.
- Анулюйце гатоўнасць пры паўторным падключэнні, выхадзе або страце канала назірання.
- Правярайце прыватны рэсурс толькі пасля таго, як уладальнік паведаміць пра завяршэнне ініцыялізацыі.
- Публікуйце толькі вынікі, якія ўсё яшчэ належаць бягучаму пакаленню.
Страчаны канал азначае невядома, а не адключана. Паведамляць пра невядомае сумленней, чым паведамляць пра адключэнне, якое вы не можаце даказаць, і гэта не дае логіцы паўторных спроб дзейнічаць на здагадку.
Паведамляйце доказы, а не лагічнае значэнне
Вяртайце названы стан сеанса і гатоўнасць рэсурсу як асобныя значэнні, кожнае з узростам. Тады спажывец, якому трэба нешта вырашыць, бачыць, што апошняя паспяховая праверка мае ўзрост сорак секунд, а гэта іншая інфармацыя, чым свежая.
Пошук публічнага IP не з'яўляецца доказам таго, што прыватны API выкарыстаў патрабаваны маршрут, таму не падавайце гэта як доказ.
Трымайце прымус па-за назіральнікам
Зваротны выклік выконваецца пасля факту. Ён не можа гарантаваць, што паміж падзеяй і апрацоўшчыкам з машыны не выйшаў ні адзін пакет. Калі праект патрабуе блакавання, яго павінна забяспечыць сеткавая палітыка, а задача назіральніка — растлумачыць доказы свайму выклікальніку. Змешванне двух дае код, які лічыць сябе файрволам, бо трымае лагічнае значэнне. Трымайце ўзровень прымусу нудным і тэставаным, а назіральнік хай застаецца чытачом.
Тады пытанне UI становіцца простым: пакажыце тое, што вядома, з яго ўзростам, і кажыце «невядома», калі гэта так.