Android VpnService: TUN o'qishining to'xtab qolishi ENOBUFS yozish xatolariga qarshi
Hech qachon qaytmaydigan o'qish va ENOBUFS bilan muvaffaqiyatsiz bo'ladigan yozish qarama-qarshi sabablarga ega. Tuzatishni tanlash uchun e'lon qilingan oynani va paket uzunligini o'lchang.
Bitta VpnService tsiklida ikkita alomat paydo bo'ladi va ikkalasi ham bir xil narsada ayblanadi: hech qachon qaytmaydigan o'quvchi va ENOBUFS bilan muvaffaqiyatsiz bo'ladigan yozish. Bular bitta xatoning variantlari emas. To'xtab qolgan o'qish peer bilan oqim nazorati bo'yicha kelishmovchilik; muvaffaqiyatsiz yozish interfeys qabul qilmaydigan paket. Agar ularni bitta muammo deb qarasangiz, hech qachon ishtirok etmagan buferlarni sozlashga bir kun sarflaysiz. Quyidagi bo'linish tsiklning qaysi yarmini birinchi o'lchashni qanday hal qilishimdir.
Bitta tsikl, ikkita mustaqil nosozlik nuqtasi
Xizmatingiz TUN fayl deskriptoridan IP paketini o'qiydi, tekshiradi va o'sha deskriptorga javob yozadi. Bu yo'lda buzilgan hamma narsa o'sha ikkita chaqiruvdan birida ko'rinadi, shuning uchun ikkalasi ham tunnel buzilgan deb xabar qilinadi. Chaqiruvlarning shartnomalari boshqacha. O'qish yadro sizga paket berishini kutadi, yozish esa yadroga rad etish huquqi bor paketni beradi.
O'qishlar o'ylash uchun oson yarmi. Hech narsa qaytarmaydigan o'qish yo hech qachon kelmaydigan trafikni kutadi, yo peer yuborishni to'xtatgani uchun kutadi.
To'xtab qolgan o'qish oqim nazoratidagi kelishmovchilik
TUN qurilmasini o'qish paket mavjud bo'lguncha bloklaydi. Bu loyihalashtirilgan xatti-harakat, shuning uchun read ichida turgan oqim bo'sh kanalda normal ko'rinadi. Bu osilib qolishga aylanadi, agar sizning TCP stekingiz mahalliy ilovadan ma'lumot qabul qilishda davom etsa, lekin uzoq peer ko'proq yuborishi uchun yetarli oyna e'lon qilmasa. Peer siz e'lon qilgan oynani to'ldiradi, ACK va oyna yangilanishini kutadi va to'xtaydi. Shunda o'quvchingizda qaytarish uchun hech narsa qolmaydi, ilova esa ochiq va jim ulanishni ko'radi.
Odatiy sabablar e'lon qilingan oynani kuzatmasdan soketga yozadigan stek va buferini bo'shatgandan keyin oyna yangilanishini yuborishni unutadigan stekdir. Tashqaridan ikkalasi bir xil ko'rinadi: baytlar chiqadi, hech narsa qaytmaydi, xato ko'tarilmaydi. O'qish oqimini tahlil qilish, DNS hal qilish yoki disk I/O paytida bloklash bog'liq bo'lmagan sababdan xuddi shu shakldagi alomatni keltirib chiqaradi, shuning uchun oqim nazoratini ayblashdan oldin oqim haqiqatan read ichida ekanini tasdiqlang.
Peerning yuborish navbatini va u oxirgi ko'rgan oynani tekshiring. Agar peer sizning oynangiz nol bo'lgani uchun ma'lumotni ushlab turgan bo'lsa, tuzatish sizning ACK yo'lingizda, TUN o'qishida emas.
ENOBUFS o'lcham muammosi, bufer yetishmovchiligi emas
TUN interfeysida ENOBUFS qaytaradigan yozish odatda paket uzunligini rad etadi. Interfeysda MTU bor, undan katta paketni navbatga qo'yib bo'lmaydi va o'sha paketni qayta urinish har safar bir xil muvaffaqiyatsiz bo'ladi. Bu determinizm belgidir. Haqiqiy bufer yetishmovchiligi bosim pasayganda yo'qoladi, o'lcham rad etishi esa qancha kutishingizga ahamiyat bermaydi.
TCPda aynan shu uchun mexanizm bor va bu odamlar o'tkazib yuboradigan qadam: MSS cheklovi. Foydalanuvchi maydonida segmentlarni sintez qilganda foydali yuk o'lchamini o'zingiz tanlaysiz va uni tunnel MTU o'rniga mahalliy interfeys MTU dan olish tunnel ko'tara olmaydigan segmentlarni chiqaradi. Ikkisining kichigiga cheklang. Cheklov faqat TCPni qamrab oladi, shuning uchun juda katta UDP datagrammani kodingiz bo'laklashi yoki tashlashi kerak.
Nosozlikni tuzatish paytida yozgan har bir paketingiz uzunligini jurnalga yozing. Muvaffaqiyatsiz bo'ladigan uzunlik javobdir va urinishlar orasida o'zgarmaydi.
Holatlarni ajratadigan uchta o'lchov
Ikkala nosozlikning hech biri uchun debugger kerak emas. Uchta kuzatuv sizni to'g'ri bo'limga yo'naltirish uchun yetarli va ular qayta qurishdan kamroq vaqt oladi.
| What you see | Where to look |
|---|---|
| Reader parked, peer send queue empty, last advertised window zero | Window updates in your ACK path |
| Write fails on one length and succeeds on a smaller one | MSS clamp and tunnel MTU |
| Both directions look fine, app still sees no data | Protected socket and routing loop |
Agar uchinchi qator mos kelsa, tsiklning o'zi joyida va muammo tunnel ko'ra olmaydigan joyda: o'z VPN trafikingizni tunnelga qaytarib olib ketadigan himoyalanmagan transport soketi, bu odatda muvaffaqiyatli bo'lib, keyin hech narsa yetkazmaydigan ulanish sifatida ko'rinadi.
Ikkala yo'l ham bog'liq bo'lgan quvurlarni tuzating
Interfeys to'g'ri qurilmaguncha hech qanday alomatning ortidan quvish arzimaydi. Muhim builder chaqiruvlari manzil, marshrut, MTU va rozilik, shu tartibda:
Builder()
.addAddress(10.8.0.2, 32)
.addRoute(0.0.0.0, 0)
.setMtu(1400)
.establish()MTUni namuna ishlatgan qiymatda qoldirmasdan tunnelning haqiqiy qiymatiga o'rnating. Keyin yozganlaringizning barchasi shu raqam bilan cheklangan, va siz o'rnatgan MTU bilan cheklaydigan MSS orasidagi nomuvofiqlik tuzatishdan keyin yozish nosozligini qaytarishning eng keng tarqalgan usuli. So'ngra VPN serverining o'ziga trafik olib boradigan soketni himoyalang. protect bo'lmasa, bu paketlar o'z tunnelingizga qaytariladi va ulanish hech qachon tugamaydi, bu to'xtab qolgan o'qishga o'xshaydi, lekin marshrutlash tsiklidir.
Rozilikni chetlab o'tib bo'lmaydi. prepare null qaytarmasdan boshlangan xizmat interfeys olmaydi va establish istisno tashlash o'rniga null qaytaradi, shuning uchun tsiklni boshlashdan oldin deskriptorni tekshiring. VpnService ma'lumotnomasi namunani ko'chirish o'rniga builder shartnomasini tasdiqlaydigan joy.
Bir vaqtning o'zida bitta yo'nalishni tekshiring
TCPga tegishdan oldin tunnel orqali UDP aks-sadosini yuboring va paket uzunliklarini kuzating. Keyin qo'l siqishni oching va segmentlar siz hisoblagan MSS dan pastda qolishini tasdiqlang. Ulanishlarni emas, paketlarni sanang, shunda to'xtash osilib qolgan soket o'rniga hisoblagichda bo'shliq sifatida ko'rinadi. Yozishlar portlashidan keyin o'qishlar to'xtasa, oyna aybdor; yozishlar darhol muvaffaqiyatsiz bo'lsa, uzunlik.
Keyin qurilmani qayta ishga tushiring va bir marta takrorlang, chunki faqat birinchi tarmoq o'zgarishigacha ishlaydigan VpnService oqim nazorati emas, marshrutlash muammosidir.