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 の問いは単純になります。分かっていることをその経過時間とともに示し、分からないときは分からないと言うのです。