एकाच VpnService लूपमध्ये दोन लक्षणे दिसतात आणि दोन्हींना एकाच गोष्टीला दोष दिला जातो: कधीही न परतणारा वाचक, आणि ENOBUFS ने अयशस्वी होणारे लेखन. हे एकाच बगचे प्रकार नाहीत. अडकलेले वाचन म्हणजे पीअरसोबत प्रवाह नियंत्रणाबाबत मतभेद; अयशस्वी लेखन म्हणजे इंटरफेस स्वीकारणार नाही असा पॅकेट. जर तुम्ही त्यांना एक समस्या मानले, तर कधीही सहभागी न झालेले बफर ट्यून करण्यात तुम्ही एक दिवस घालवाल. खालील विभाजन म्हणजे लूपचा कोणता अर्धा भाग आधी मोजायचा हे मी कसे ठरवतो.

एक लूप, दोन स्वतंत्र अयशस्वी बिंदू

तुमची सेवा TUN फाइल डिस्क्रिप्टरमधून IP पॅकेट वाचते, त्याची तपासणी करते आणि त्याच डिस्क्रिप्टरमध्ये उत्तर लिहिते. त्या मार्गात जे काही तुटते ते त्या दोन कॉलपैकी एकात दिसते, म्हणून दोन्हींची नोंद टनेल तुटले अशी केली जाते. कॉलचे करार वेगळे आहेत. वाचन कर्नेल तुम्हाला पॅकेट देण्याची वाट पाहते, आणि लेखन कर्नेलला नाकारण्याचा अधिकार असलेले पॅकेट देते.

वाचन हा विचार करण्यास सोपा अर्धा भाग आहे. जे वाचन काहीही परत देत नाही ते एकतर कधीही न येणाऱ्या रहदारीची वाट पाहत असते किंवा पीअरने पाठवणे थांबवल्यामुळे वाट पाहत असते.

अडकलेले वाचन म्हणजे प्रवाह नियंत्रणाबाबत मतभेद

TUN डिव्हाइस वाचणे पॅकेट उपलब्ध होईपर्यंत ब्लॉक करते. हे डिझाइन केलेले वर्तन आहे, आणि म्हणूनच read मध्ये थांबलेला थ्रेड निष्क्रिय लिंकवर सामान्य दिसतो. जेव्हा तुमचा स्वतःचा TCP स्टॅक स्थानिक अॅपकडून डेटा स्वीकारत राहतो पण दूरच्या पीअरला अधिक पाठवण्यासाठी पुरेशी विंडो कधीही जाहीर करत नाही, तेव्हा ते हँग होते. पीअर तुम्ही जाहीर केलेली विंडो भरते, ACK आणि विंडो अद्यतनाची वाट पाहते, आणि थांबते. मग तुमच्या वाचकाकडे परत देण्यासारखे काही नसते, आणि अॅपला उघडे पण शांत कनेक्शन दिसते.

सामान्य कारणे म्हणजे जाहीर केलेल्या विंडोचा मागोवा न घेता सॉकेटमध्ये लिहिणारा स्टॅक, आणि आपला बफर रिकामा केल्यानंतर विंडो अद्यतन पाठवायचे विसरणारा स्टॅक. बाहेरून दोन्ही सारखेच दिसतात: बाइट बाहेर जातात, काहीही परत येत नाही, त्रुटी उठत नाही. वाचन थ्रेडला पार्सिंग, DNS रिझोल्यूशन किंवा डिस्क I/O वर ब्लॉक केल्यास असंबंधित कारणाने त्याच आकाराचे लक्षण तयार होते, म्हणून प्रवाह नियंत्रणाला दोष देण्यापूर्वी थ्रेड खरोखर read मध्ये आहे याची खात्री करा.

पीअरची पाठवण्याची रांग आणि त्याने शेवटी पाहिलेली विंडो तपासा. तुमची विंडो शून्य असल्यामुळे पीअर डेटा धरून ठेवत असेल, तर दुरुस्ती तुमच्या ACK मार्गात आहे, TUN वाचनात नाही.

ENOBUFS ही आकाराची समस्या आहे, बफरची कमतरता नाही

TUN इंटरफेसवर ENOBUFS परत देणारे लेखन सामान्यतः पॅकेटची लांबी नाकारते. इंटरफेसला MTU असते, त्यापेक्षा मोठे पॅकेट रांगेत टाकता येत नाही, आणि तोच पॅकेट पुन्हा प्रयत्न केल्यास प्रत्येक वेळी त्याच पद्धतीने अयशस्वी होते. हा निश्चितपणा हाच संकेत आहे. खरी बफर कमतरता दाब कमी झाल्यावर निघून जाते, पण आकाराचे नाकारणे तुम्ही किती काळ वाट पाहाल याची पर्वा करत नाही.

TCP मध्ये यासाठीच एक यंत्रणा आहे, आणि तेच लोक वगळतात ते पाऊल: MSS क्लॅम्प. जेव्हा तुम्ही वापरकर्ता जागेत सेगमेंट तयार करता तेव्हा पेलोडचा आकार तुम्ही निवडता, आणि तो टनेलच्या MTU ऐवजी स्थानिक इंटरफेसच्या MTU मधून घेतल्यास टनेल वाहून नेऊ शकत नाहीत असे सेगमेंट बाहेर पडतात. दोन्हींपैकी लहानावर मर्यादा घाला. क्लॅम्प फक्त TCP ला व्यापते, म्हणून खूप मोठा UDP डेटाग्राम तुमच्या कोडने विभाजित करावा किंवा टाकून द्यावा.

डीबग करताना तुम्ही लिहिता त्या प्रत्येक पॅकेटची लांबी नोंदवा. जी लांबी अयशस्वी होते तेच उत्तर आहे, आणि ते प्रयत्नांमध्ये बदलणार नाही.

प्रकरणे वेगळी करणारी तीन मोजमापे

दोन्हीपैकी कोणत्याही अयशस्वीतेसाठी तुम्हाला डीबगरची गरज नाही. तुम्हाला योग्य विभागाकडे नेण्यासाठी तीन निरीक्षणे पुरेशी आहेत, आणि ती पुनर्बांधणीपेक्षा कमी वेळ घेतात.

What you seeWhere to look
Reader parked, peer send queue empty, last advertised window zeroWindow updates in your ACK path
Write fails on one length and succeeds on a smaller oneMSS clamp and tunnel MTU
Both directions look fine, app still sees no dataProtected socket and routing loop

जर तिसरी ओळ जुळली, तर लूप स्वतःच ठीक आहे आणि समस्या तिथे आहे जिथे टनेलला ती दिसू शकत नाही: तुमचा स्वतःचा VPN रहदारी टनेलमध्ये परत नेणारा असुरक्षित ट्रान्सपोर्ट सॉकेट, जो सामान्यतः यशस्वी होऊन नंतर काहीही पोहोचवत नाही असे कनेक्शन म्हणून दिसतो.

दोन्ही मार्ग ज्या प्लंबिंगवर अवलंबून आहेत ते दुरुस्त करा

इंटरफेस योग्यरित्या बांधल्याशिवाय कोणत्याही लक्षणाचा पाठलाग करणे योग्य नाही. महत्त्वाचे builder कॉल म्हणजे पत्ता, मार्ग, MTU आणि संमती, या क्रमाने:

Builder()
  .addAddress(10.8.0.2, 32)
  .addRoute(0.0.0.0, 0)
  .setMtu(1400)
  .establish()

MTU नमुन्याने वापरलेल्या मूल्यावर ठेवण्याऐवजी टनेलच्या खऱ्या मूल्यावर सेट करा. नंतर तुम्ही जे लिहाल ते त्या संख्येने मर्यादित आहे, आणि तुम्ही सेट करता तो MTU व ज्यावर मर्यादा घालता तो MSS यांतील तफावत दुरुस्तीनंतर लेखन अयशस्वीता पुन्हा आणण्याचा सर्वात सामान्य मार्ग आहे. मग VPN सर्व्हरकडे रहदारी नेणाऱ्या सॉकेटचे संरक्षण करा. protect नसल्यास ती पॅकेट्स तुमच्याच टनेलमध्ये परत राउट होतात आणि कनेक्शन कधीही पूर्ण होत नाही, जे अडकलेल्या वाचनासारखे दिसते पण राउटिंग लूप आहे.

संमती टाळता येत नाही. prepare null न परतवता सुरू केलेल्या सेवेला इंटरफेस मिळत नाही, आणि establish फेकण्याऐवजी null परत देते, म्हणून लूप सुरू करण्यापूर्वी डिस्क्रिप्टर तपासा. VpnService संदर्भ म्हणजे नमुना कॉपी करण्याऐवजी builder कराराची पुष्टी करण्याची जागा.

एका वेळी एक दिशा तपासा

TCP ला स्पर्श करण्यापूर्वी टनेलमधून UDP प्रतिध्वनी पाठवा आणि पॅकेटच्या लांबी पहा. मग हँडशेक उघडा आणि सेगमेंट तुम्ही मोजलेल्या MSS खाली राहतात याची खात्री करा. कनेक्शनऐवजी पॅकेट मोजा, जेणेकरून अडथळा अडकलेल्या सॉकेटऐवजी काउंटरमधील अंतर म्हणून दिसेल. लेखनाच्या लाटेनंतर वाचन थांबले तर विंडो संशयित आहे; लेखन लगेच अयशस्वी झाले तर लांबी.

मग डिव्हाइस रीबूट करा आणि एकदा पुन्हा करा, कारण पहिल्या नेटवर्क बदलापर्यंतच कार्य करणारे VpnService प्रवाह नियंत्रणाची नाही, राउटिंगची समस्या आहे.

संबंधित लेख