ناظر وضعیت OpenVPN در C#: نشست را بپایید، نه کارت شبکه را
NetworkAvailabilityChanged رویداد VPN نیست. جریان وضعیت را از کلاینت مالک بخوانید، از بررسیهای کهنه محافظت کنید و آمادگی را برای هر اتصال منتشر کنید.
برنامه شما در حالی که تونل میمیرد به کار خود ادامه میدهد. وایفای هنوز فعال است، اینترنت هنوز پاسخ میدهد و API خصوصی که برنامه به آن وابسته است غیرقابل دسترس شده است. رویداد .NET که مردم معمولاً اینجا به سراغش میروند، NetworkAvailabilityChanged، اصلاً آن وضعیت را توصیف نمیکند. این رویداد تغییری در وضعیت تجمعی ماشین را گزارش میکند و آن وضعیت میتواند در طول قطع و اتصال مجدد VPN درست بماند. ناظر باید کلاینتی را بپاید که صاحب تونل است، نه خلاصه پشته شبکه را.
دسترسپذیری یک مقدار تجمعی است، نه تونل شما
NetworkAvailabilityChanged زمانی فعال میشود که پاسخ دسترسپذیری تجمعی ماشین تغییر کند و آن پاسخ هر رابط فعالی جز loopback یا تونل را میشمارد.
قطع VPN در حالی که وایفای فعال است هیچچیز از آنچه اندازه میگیرد را تغییر نمیدهد، به همین دلیل رویداد میتواند درست در همان خرابیای که برایتان مهم است ساکت بماند.
جریان وضعیت از آن کلاینتی است که صاحب تونل است
مشخص کنید کدام مؤلفه صاحب اتصال است: رابط گرافیکی یا سرویس OpenVPN، اپ OpenVPN Connect، یا فرآیندی که برنامه شما آغاز و کنترل میکند. اینها مالکان متفاوت با رابطهای پشتیبانیشده متفاوت هستند و ناظر باید از رابطی استفاده کند که با استقرار سازگار است. برای فرآیند OpenVPN که مجاز به یکپارچهسازی با آن هستید، رابط مدیریت وضعیت جاری، تاریخچه و اعلانهای زمان واقعی را گزارش میدهد. پیامهای ساختیافته مستند را تحلیل کنید، نه اینکه در عنوان رابط گرافیکی بومیسازیشده واژه connected را جستجو کنید.
کانال مدیریت یک سطح کنترل با امتیاز است، پس شنونده تازه باز نکنید و با رابط گرافیکی بر سر کانالی که خودش دارد کشمکش نکنید.
محافظهای نسل نتایج کهنه را متوقف میکنند
هر مشاهده به یک تلاش اتصال مشخص تعلق دارد. وقتی تلاش تازهای آغاز میشود، وقتی کانال از دست میرود و وقتی کلاینت خروج را گزارش میدهد، شمارنده نسل را افزایش دهید.
یک بررسی را در نظر بگیرید که در طول اتصال A آغاز شده است. تونل میافتد، اتصال B آغاز میشود و سپس موفقیت تأخیری HTTP متعلق به A میرسد. بدون محافظ، آن نتیجه قدیمی نمایش را سبز میکند در حالی که B هنوز احراز هویت میکند. لغو، کار هدررفته را کاهش میدهد و بررسی نسل همان چیزی است که مانع میشود یک تکمیل کهنه به وضعیت جاری تبدیل شود:
var generation = _generation;
var reachable = await ProbeAsync(token);
if (generation != _generation) return;
Publish(reachable);
همین قاعده را پیرامون ارسال به رابط کاربری اعمال کنید، چون بهروزرسانی مارشالشده میتواند پس از جلو رفتن نسل برسد.
راهاندازی و اتصال مجدد یک چرخه عمر واحدند
اتصال اول، اتصال مجدد و از دست رفتن کانال را یک چرخه عمر با یک مجموعه قاعده در نظر بگیرید:
- وضعیت جاری را بخوانید و در جایی که رابط اجازه میدهد، در یک مرحله در اعلانها مشترک شوید.
- هر مشاهده را با شناسه اتصال و نسل نشانهگذاری کنید.
- آمادگی را در اتصال مجدد، خروج یا کانال مشاهده از دست رفته باطل کنید.
- منبع خصوصی را تنها پس از آن بررسی کنید که مالک اتمام راهاندازی را گزارش دهد.
- فقط نتایجی را منتشر کنید که هنوز به نسل جاری تعلق دارند.
کانال از دست رفته یعنی ناشناخته، نه قطعشده. گزارش ناشناخته صادقانهتر از گزارش قطعی است که نمیتوانید اثباتش کنید و مانع میشود منطق تلاش مجدد بر اساس گمان عمل کند.
شواهد را گزارش کنید، نه یک مقدار بولی
وضعیت نامدار نشست و آمادگی منبع را بهصورت دو مقدار جداگانه برگردانید که هر کدام سنی دارند. مصرفکنندهای که باید چیزی را تصمیم بگیرد آنگاه میبیند آخرین بررسی موفق چهل ثانیه سن دارد، که اطلاعاتی متفاوت از یک بررسی تازه است.
جستجوی IP عمومی شاهدی بر این نیست که یک API خصوصی از مسیر لازم استفاده کرده است، پس آن را طوری ارائه نکنید که گویی هست.
اعمال را بیرون از ناظر نگه دارید
فراخوانی پس از وقوع اجرا میشود. نمیتواند تضمین کند که هیچ بستهای بین رویداد و گرداننده از ماشین خارج نشده است. اگر طراحی مسدودسازی میخواهد، سیاست شبکه باید آن را فراهم کند و وظیفه ناظر توضیح شواهد به فراخواننده است. آمیختن این دو کدی میسازد که چون یک بولی دارد خود را دیوار آتش میپندارد. لایه اعمال را خستهکننده و آزمونپذیر نگه دارید و بگذارید ناظر خواننده بماند.
آنگاه پرسش رابط کاربری ساده میشود: آنچه دانسته است را با سنش نشان دهید و وقتی ناشناخته است بگویید ناشناخته.