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-এর প্রশ্নটি সহজ হয়ে যায়: যা জানা আছে তা তার বয়সসহ দেখান, আর অজানা হলে অজানা বলুন।