Նույն VpnService ցիկլում հայտնվում են երկու ախտանիշ և մեղադրվում է նույն բանը՝ ընթերցող, որը երբեք չի վերադառնում, և գրառում, որը ձախողվում է ENOBUFS-ով։ Դրանք մեկ սխալի տարբերակներ չեն։ Կանգ առած ընթերցումը հոսքի կառավարման շուրջ անհամաձայնություն է զույգի հետ, իսկ ձախողված գրառումը փաթեթ է, որը միջերեսը չի ընդունի։ Եթե դրանք դիտարկեք որպես մեկ խնդիր, մի օր կանցկացնեք բուֆերներ կարգավորելով, որոնք երբեք ներգրավված չեն եղել։ Ստորև բերված բաժանումն այն է, թե ինչպես եմ որոշում ցիկլի որ կեսը նախ չափել։

Մեկ ցիկլ, երկու անկախ ձախողման կետ

Ձեր ծառայությունը IP փաթեթ է կարդում TUN ֆայլի նկարագրիչից, ստուգում այն և պատասխան գրում նույն նկարագրիչին։ Այդ ուղու վրա ինչ որ կոտրվում է, ի հայտ է գալիս այդ երկու կանչերից մեկում, դրա համար էլ երկուսն էլ ներկայացվում են որպես կոտրված թունել։ Կանչերն ունեն տարբեր պայմանագրեր։ Ընթերցումը սպասում է, որ միջուկը ձեզ փաթեթ տա, իսկ գրառումը միջուկին հանձնում է փաթեթ, որը նա իրավունք ունի մերժելու։

Ընթերցումները մտածելու համար հեշտ կեսն են։ Ընթերցումը, որը ոչինչ չի վերադարձնում, կամ սպասում է երբեք չեկող երթևեկության, կամ սպասում է, որովհետև զույգը դադարեցրել է ուղարկելը։

Կանգ առած ընթերցումը հոսքի կառավարման անհամաձայնություն է

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-ի պայմանագիրը՝ օրինակը պատճենելու փոխարեն։

Ստուգեք մեկ ուղղություն միաժամանակ

Ուղարկեք UDP արձագանք թունելով և դիտեք փաթեթների երկարությունները նախքան TCP-ին դիպչելը։ Այնուհետև բացեք ձեռքսեղմում և հաստատեք, որ սեգմենտները մնում են ձեր հաշվարկած MSS-ից ցածր։ Հաշվեք փաթեթներ, ոչ թե կապեր, որպեսզի կանգառը երևա որպես բացթողում հաշվիչում, այլ ոչ թե կախված սոկետ։ Եթե գրառումների պոռթկումից հետո ընթերցումները դադարում են, պատուհանն է կասկածելին, եթե գրառումները անմիջապես ձախողվում են՝ երկարությունը։

Այնուհետև վերագործարկեք սարքը և կրկնեք մեկ անգամ, որովհետև VpnService-ը, որը աշխատում է միայն մինչև ցանցի առաջին փոփոխությունը, երթուղավորման խնդիր է, ոչ թե հոսքի կառավարման։

Առնչվող հոդվածներ