צופה במצב OpenVPN ב-C#: עקבו אחרי הסשן, לא אחרי כרטיס הרשת
NetworkAvailabilityChanged אינו אירוע VPN. קראו את זרם המצב של הלקוח הבעלים, הגנו מפני בדיקות מיושנות ופרסמו מוכנות לכל חיבור.
האפליקציה שלכם ממשיכה לעבוד בזמן שהמנהרה מתה. ה-Wi-Fi עדיין פועל, האינטרנט עדיין עונה, וה-API הפרטי שהאפליקציה תלויה בו הפך לבלתי נגיש. אירוע ה-.NET שאליו נוהגים לפנות כאן, NetworkAvailabilityChanged, כלל אינו מתאר את המצב הזה. הוא מדווח על שינוי במצב המצטבר של המכונה, ומצב זה יכול להישאר נכון לאורך ניתוק והתחברות מחדש של VPN. צופה חייב לעקוב אחרי הלקוח שבעל המנהרה, ולא אחרי הסיכום של מחסנית הרשת.
זמינות היא ערך מצטבר, לא המנהרה שלכם
NetworkAvailabilityChanged מופעל כאשר התשובה המצטברת של המכונה על זמינות משתנה, ותשובה זו סופרת כל ממשק פעיל מלבד loopback או מנהרה.
נפילת VPN בזמן שה-Wi-Fi פעיל אינה משנה דבר ממה שהיא מודדת, ולכן האירוע יכול להישאר שקט בדיוק בתקלת הכשל שחשובה לכם.
זרם המצב שייך ללקוח שבעל המנהרה
זהו איזה רכיב בעל החיבור: ממשק ה-GUI או השירות של OpenVPN, OpenVPN Connect, או תהליך שהאפליקציה שלכם הפעילה ושולטת בו. אלה בעלים שונים עם ממשקים נתמכים שונים, והצופה חייב להשתמש בזה שמתאים לפריסה. עבור תהליך OpenVPN שאתם מורשים להשתלב בו, ממשק הניהול מדווח על המצב הנוכחי, היסטוריה והתראות בזמן אמת. נתחו את ההודעות המובנות המתועדות במקום לחפש את המילה connected בכיתוב GUI מתורגם.
ערוץ הניהול הוא משטח שליטה בעל הרשאה, לכן אל תפתחו מאזין חדש ואל תילחמו עם ה-GUI על ערוץ שכבר ברשותו.
שומרי דור עוצרים תוצאות מיושנות
כל תצפית שייכת לניסיון חיבור מסוים. הגדילו מונה דור כאשר מתחיל ניסיון חדש, כאשר הערוץ אובד וכאשר הלקוח מדווח על יציאה.
שקלו בדיקה שהחלה במהלך חיבור A. המנהרה נופלת, חיבור B מתחיל, ואז מגיעה הצלחת ה-HTTP המאוחרת של A. בלי שומר, התוצאה הישנה הופכת את התצוגה לירוקה בזמן ש-B עדיין מאמת. ביטול מפחית עבודה מבוזבזת, ובדיקת הדור היא מה שמונע מהשלמה מיושנת להפוך למצב הנוכחי:
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
החילו את אותו כלל סביב השליחה ל-UI, כי העדכון הממוראל יכול להגיע לאחר שהדור התקדם.
אתחול והתחברות מחדש הם מחזור חיים אחד
התייחסו לחיבור הראשון, להתחברות מחדש ולאובדן הערוץ כמחזור חיים אחד עם סט חוקים אחד:
- קראו את המצב הנוכחי והירשמו להתראות בשלב אחד היכן שהממשק מאפשר.
- תייגו כל תצפית בזהות החיבור ובדור.
- בטלו את המוכנות בהתחברות מחדש, ביציאה או בערוץ תצפית שאבד.
- בדקו את המשאב הפרטי רק לאחר שהבעלים מדווח שהאתחול הושלם.
- פרסמו רק תוצאות שעדיין שייכות לדור הנוכחי.
ערוץ שאבד פירושו לא ידוע, לא מנותק. דיווח על לא ידוע הוא כן יותר מדיווח על ניתוק שאינכם יכולים להוכיח, והוא מונע מהלוגיקה של ניסיון חוזר לפעול לפי ניחוש.
דווחו ראיות, לא ערך בוליאני
החזירו את מצב הסשן הנקוב ואת מוכנות המשאב כערכים נפרדים, כל אחד עם גיל. צרכן שצריך להחליט משהו יכול אז לראות שהבדיקה המוצלחת האחרונה בת ארבעים שניות, וזה מידע שונה ממידע טרי.
חיפוש IP ציבורי אינו ראיה לכך שה-API פרטי השתמש במסלול הנדרש, לכן אל תציגו אותו כאילו כן.
השאירו את האכיפה מחוץ לצופה
Callback רץ בדיעבד. הוא אינו יכול להבטיח שאף מנה לא עזבה את המכונה בין האירוע למטפל. אם התכנון דורש חסימה, מדיניות הרשת חייבת לספק אותה, ותפקיד הצופה הוא להסביר את הראיות לקורא שלו. ערבוב של השניים מייצר קוד שחושב שהוא חומת אש מפני שיש לו ערך בוליאני. השאירו את שכבת האכיפה משעממת ובת-בדיקה, ותנו לצופה להישאר קורא.
אז שאלת ה-UI נעשית פשוטה: הראו מה ידוע, עם הגיל שלו, ואמרו לא ידוע כשזה המצב.