Амалисозии протоколи боэътимоди Udp барои .Net

Интернет кайҳо пештар тағйир ёфтааст. Яке аз протоколҳои асосии Интернет - UDP аз ҷониби барномаҳо на танҳо барои интиқоли датаграммаҳо ва пахшҳо, балки инчунин барои таъмини пайвастҳои "ҳамсол ба ҳамсол" байни гиреҳҳои шабака истифода мешавад. Аз сабаби тарҳи оддии худ, ин протокол бисёр истифодаҳои қаблан ба нақша гирифтанашуда дорад, аммо камбудиҳои протокол, аз қабили набудани интиқоли кафолатнок, дар ҳеҷ ҷо аз байн нарафтанд. Ин мақола татбиқи протоколи интиқоли кафолатнокро тавассути UDP тавсиф мекунад.
Мундариҷа:даромад
Талаботи протокол
Сарлавҳаи боэътимоди UDP
Принсипҳои умумии протокол
Вақтсанҷиҳо ва таймерҳои протокол
Диаграммаи ҳолати интиқоли боэътимоди UDP
Ба код амиқтар. воҳиди идоракунии интиқол
Ба код амиқтар. давлатхо

Ба код амиқтар. Эҷод ва таъсиси робитаҳо
Ба код амиқтар. Бастани пайвастшавӣ дар вақти тамомшавӣ
Ба код амиқтар. Барқарор кардани интиқоли маълумот
API-и боэътимоди UDP
хулоса
Истинодҳо ва мақолаҳои муфид

даромад

Меъмории аслии Интернет фазои суроғаи якхеларо дар назар дошт, ки дар он ҳар як гиреҳ суроғаи глобалӣ ва беназири IP дошт ва метавонад мустақиман бо дигар гиреҳҳо муошират кунад. Ҳоло Интернет, воқеан, як меъмории дигар дорад - як минтақаи суроғаҳои глобалии IP ва бисёр минтақаҳо бо суроғаҳои хусусӣ дар паси дастгоҳҳои NAT пинҳон шудаанд.Дар ин меъморӣ, танҳо дастгоҳҳо дар фазои суроғаи глобалӣ метавонанд ба осонӣ бо касе дар шабака муошират кунанд, зеро онҳо суроғаи IP-и беназири дар саросари ҷаҳон масир доранд. Гиреҳ дар шабакаи хусусӣ метавонад ба дигар гиреҳҳои ҳамон шабака пайваст шавад ва инчунин метавонад ба дигар гиреҳҳои маъруф дар фазои суроғаи ҷаҳонӣ пайваст шавад. Ин ҳамкорӣ асосан аз ҳисоби механизми тарҷумаи суроғаҳои шабакавӣ ба даст оварда мешавад. Дастгоҳҳои NAT, ба монанди роутерҳои Wi-Fi, барои пайвастҳои содиротӣ сабтҳои махсуси ҷадвали тарҷумаро эҷод мекунанд ва суроғаҳои IP ва рақамҳои портро дар бастаҳо тағир медиҳанд. Ин имкон медиҳад, ки пайвастҳои содиротӣ аз шабакаи хусусӣ ба ҳостҳо дар фазои суроғаи глобалӣ. Аммо дар айни замон, дастгоҳҳои NAT одатан ҳама трафики воридотро маҳкам мекунанд, агар қоидаҳои алоҳида барои пайвастҳои воридотӣ муқаррар карда нашуда бошанд.

Ин меъмории Интернет барои муоширати муштарӣ ва сервер кофӣ дуруст аст, ки дар он муштариён метавонанд дар шабакаҳои хусусӣ бошанд ва серверҳо суроғаи глобалӣ доранд. Аммо он барои пайвасти мустақими ду гиреҳ байни онҳо мушкилот эҷод мекунад гуногун шабакаҳои хусусӣ. Пайвастагии мустақим байни ду гиреҳ барои барномаҳои ҳамсол ба ҳамсол ба монанди интиқоли овоз (Skype), дастрасии дурдаст ба компютер (TeamViewer) ё бозии онлайн муҳим аст.

Яке аз усулҳои муассир барои барқарор кардани пайвасти ҳамсол ба ҳамсол байни дастгоҳҳо дар шабакаҳои хусусии мухталифи сӯрохӣ номида мешавад. Ин техника бештар бо замимаҳо дар асоси протоколи UDP истифода мешавад.

Аммо агар аризаи шумо интиқоли кафолатноки маълумотро талаб кунад, масалан, шумо файлҳоро байни компютерҳо интиқол медиҳед, пас истифодаи UDP бо сабаби он, ки UDP протоколи таҳвили кафолатнок нест ва бар хилофи TCP интиқоли бастаҳоро бо тартиб таъмин намекунад, мушкилоти зиёде хоҳад дошт. протокол.

Дар ин ҳолат, барои таъмини интиқоли кафолатноки бастаҳо, татбиқи протоколи қабати барномавӣ лозим аст, ки функсияи заруриро таъмин мекунад ва тавассути UDP кор мекунад.

Ман мехоҳам фавран қайд намоям, ки техникаи сӯрохиҳои TCP барои барқарор кардани пайвастҳои TCP байни гиреҳҳо дар шабакаҳои хусусии гуногун вуҷуд дорад, аммо аз сабаби набудани дастгирии он аз ҷониби бисёре аз дастгоҳҳои NAT, он одатан ҳамчун роҳи асосии пайвастшавӣ ҳисобида намешавад. чунин гиреҳҳо.

Дар қисми боқимондаи ин мақола, ман танҳо ба татбиқи протоколи кафолати интиқол тамаркуз мекунам. Татбиқи техникаи сӯрохиҳои UDP дар мақолаҳои зерин тавсиф карда мешавад.

Талаботи протокол

  1. Интиқоли боэътимоди бастаҳо тавассути механизми бозгашти мусбӣ амалӣ карда мешавад (ба ном эътирофи мусбӣ)
  2. Зарурати интиқоли самараноки додаҳои калон, яъне. протокол бояд аз интиқоли пакетҳои нолозим канорагирӣ кунад
  3. Механизми тасдиқи интиқол бояд бекор карда шавад (қобилияти кор кардан ҳамчун протоколи "соф" UDP)
  4. Қобилияти татбиқи ҳолати фармон, бо тасдиқи ҳар як паём
  5. Воҳиди асосии интиқоли маълумот тавассути протокол бояд паём бошад

Ин талаботҳо асосан бо талаботҳои Протоколи маълумоти боэътимод, ки дар он тавсиф шудаанд, мувофиқат мекунанд RF 908 и RF 1151, ва ман ҳангоми таҳияи ин протокол ба он стандартҳо такя кардам.

Барои фаҳмидани ин талабот, биёед ба вақти интиқоли маълумот байни ду гиреҳи шабакавӣ бо истифода аз протоколҳои TCP ва UDP назар кунем. Бигзор дар ҳарду ҳолат мо як бастаро гум кунем.
Интиқоли маълумоти ғайри интерактивӣ тавассути TCP:Амалисозии протоколи боэътимоди Udp барои .Net

Тавре ки шумо аз диаграмма мебинед, дар сурати гум шудани пакет, TCP бастаи гумшударо муайян мекунад ва бо дархости рақами сегменти гумшуда ба ирсолкунанда хабар медиҳад.
Интиқоли маълумот тавассути протоколи UDP:Амалисозии протоколи боэътимоди Udp барои .Net

UDP ягон қадами ошкоркунии талафот намеандешад. Назорати хатогиҳои интиқол дар протоколи UDP пурра масъулияти барнома аст.

Муайян кардани хатогиҳо дар протоколи TCP тавассути барқарор кардани пайвастшавӣ бо гиреҳи ниҳоӣ, нигоҳ доштани ҳолати ин пайвастшавӣ, нишон додани шумораи байтҳои фиристодашуда дар ҳар як сарлавҳаи баста ва огоҳ кардани квитансияҳо бо истифода аз рақами тасдиқ ба даст оварда мешавад.

Илова бар ин, барои беҳтар кардани кор (яъне фиристодани зиёда аз як сегмент бе гирифтани тасдиқ), протоколи TCP равзанаи интиқолро истифода мебарад - шумораи байтҳои маълумоте, ки ирсолкунандаи сегмент интизор аст.

Барои маълумоти бештар дар бораи протоколи TCP, нигаред RF 793, аз UDP ба RF 768ки дар хакикат онхо муайян карда шудаанд.

Аз гуфтаҳои боло маълум аст, ки барои эҷоди протоколи боэътимоди интиқоли паём тавассути UDP (минбаъд - UDP боэътимод), татбиқи механизмҳои интиқоли додаҳо ба TCP монанд аст. Аз ҷумла:

  • ҳолати пайвастро захира кунед
  • рақамгузории сегментро истифода баред
  • бастаҳои махсуси тасдиқкуниро истифода баред
  • механизми соддашудаи равзанаро барои баланд бардоштани қобилияти протокол истифода баред

Илова бар ин, ба шумо лозим аст:

  • сигнали оғози паём, барои ҷудо кардани захираҳо барои пайвастшавӣ
  • сигнали анҷоми паём, барои интиқоли паёми қабулшуда ба барномаи болоӣ ва озод кардани захираҳои протокол
  • ба протоколи мушаххаси пайваст иҷозат диҳед, то механизми тасдиқи интиқолро ҳамчун UDP "пок" кор кунад

Сарлавҳаи боэътимоди UDP

Ба ёд оред, ки датаграммаи UDP дар датаграммаи IP фаро гирифта шудааст. Бастаи боэътимоди UDP ба датаграммаи UDP ба таври мувофиқ "печонида шудааст".
Инкапсуляцияи боэътимоди сарлавҳаи UDP:Амалисозии протоколи боэътимоди Udp барои .Net

Сохтори сарлавҳаи боэътимоди UDP хеле содда аст:

Амалисозии протоколи боэътимоди Udp барои .Net

  • Парчамҳо - парчамҳои назорати бастаҳо
  • MessageType - навъи паём, ки аз ҷониби барномаҳои болоӣ барои обуна ба паёмҳои мушаххас истифода мешавад
  • TransmissionId - рақами интиқол дар якҷоягӣ бо суроға ва порти қабулкунанда пайвастро ба таври беназир муайян мекунад
  • Рақами баста - рақами баста
  • Опсияҳо - имконоти иловагии протокол. Дар мавриди бастаи аввал, он барои нишон додани андозаи паём истифода мешавад

Парчамҳо чунинанд:

  • FirstPacket - бастаи якуми паём
  • NoAsk - паём барои фаъол кардани механизми тасдиқ талаб намекунад
  • LastPacket - бастаи охирини паём
  • RequestForPacket - бастаи тасдиқ ё дархост барои бастаи гумшуда

Принсипҳои умумии протокол

Азбаски UDP боэътимод ба интиқоли кафолатноки паёмҳо байни ду гиреҳ нигаронида шудааст, он бояд қодир бошад, ки бо тарафи дигар робита барқарор кунад. Барои барқарор кардани пайвастшавӣ, ирсолкунанда бастаеро бо парчами FirstPacket мефиристад, ки посух ба он маънои барқарор шудани пайвастро дорад. Ҳама пакетҳои ҷавобӣ, ё ба ибораи дигар, бастаҳои тасдиқкунӣ, ҳамеша арзиши майдони PacketNumberро ба як зиёд аз арзиши бузургтарини бастаҳои бомуваффақияти PacketNumber муқаррар мекунанд. Майдони Имконот барои бастаи аввалини ирсолшуда андозаи паём аст.

Барои қатъ кардани пайвастшавӣ механизми шабеҳ истифода мешавад. Парчами LastPacket дар бастаи охирини паём гузошта мешавад. Дар бастаи ҷавоб рақами бастаи охирин + 1 нишон дода шудааст, ки барои ҷониби қабулкунанда маънои интиқоли бомуваффақияти паёмро дорад.
Диаграммаи таъсис ва қатъи пайвастшавӣ:Амалисозии протоколи боэътимоди Udp барои .Net

Вақте ки пайвастшавӣ барқарор карда мешавад, интиқоли маълумот оғоз меёбад. Маълумот дар блокҳои бастаҳо интиқол дода мешавад. Ҳар як блок, ба истиснои блоки охирин, шумораи муайяни бастаҳоро дар бар мегирад. Он ба андозаи равзанаи қабул/интиқол баробар аст. Блоки охирини маълумот метавонад бастаҳои камтар дошта бошад. Пас аз фиристодани ҳар як блок, ҷониби ирсолкунанда мунтазири тасдиқи интиқол ё дархост барои аз нав расонидани бастаҳои гумшуда интизор мешавад ва равзанаи қабул/интиқолро барои қабули ҷавобҳо кушода мегузорад. Пас аз гирифтани тасдиқи интиқоли блок, равзанаи қабул/интиқол иваз мешавад ва блоки навбатии маълумот фиристода мешавад.

Ҷониби қабулкунанда бастаҳоро қабул мекунад. Ҳар як баста тафтиш карда мешавад, то бубинад, ки оё он дар равзанаи интиқол рост меояд. Бастаҳо ва нусхаҳое, ки ба тиреза намеафтанд, филтр карда мешаванд. Зеро Агар андозаи равзана собит бошад ва барои қабулкунанда ва ирсолкунанда якхела бошад, пас дар ҳолати бе талаф расонидани блоки бастаҳо, равзана барои қабули бастаҳои блоки навбатии додаҳо гузаронида мешавад ва тасдиқи интиқол дода мешавад. фиристоданд. Агар тиреза дар мўњлати муќарраркардаи таймери корї пур нашавад, он гоњ тафтиш дар бораи он, ки бастањо нарасидаанд, оѓоз карда мешавад ва дархост барои бозрасонї фиристода мешавад.
Диаграммаи интиқоли дубора:Амалисозии протоколи боэътимоди Udp барои .Net

Вақтсанҷиҳо ва таймерҳои протокол

Якчанд сабабҳо вуҷуд доранд, ки чаро пайвастшавӣ барқарор карда намешавад. Масалан, агар тарафи қабулкунанда офлайн бошад. Дар ин ҳолат, ҳангоми кӯшиши барқарор кардани пайвастшавӣ, пайвастшавӣ бо гузашти вақт қатъ карда мешавад. Татбиқи боэътимоди UDP барои муқаррар кардани мӯҳлатҳо ду таймерро истифода мебарад. Якум, таймери корӣ барои интизории посух аз мизбони дурдаст истифода мешавад. Агар он дар тарафи ирсолкунанда оташ занад, пас бастаи охирини фиристодашуда дубора фиристода мешавад. Агар мӯҳлати таймер дар қабулкунанда ба охир расад, пас санҷиши бастаҳои гумшуда анҷом дода мешавад ва дархостҳо барои интиқол фиристода мешаванд.

Вақтсанҷи дуюм барои бастани пайвастшавӣ дар ҳолати набудани алоқа байни гиреҳҳо лозим аст. Барои ҷониби ирсолкунанда, он фавран пас аз ба охир расидани таймери корӣ оғоз меёбад ва мунтазири посух аз гиреҳи дурдаст аст. Агар дар мӯҳлати муқарраршуда ҷавобе набошад, пайвастшавӣ қатъ карда мешавад ва захираҳо озод карда мешаванд. Барои тарафи қабулкунанда вақтсанҷи бастани пайвастшавӣ пас аз ду маротиба ба охир расидани таймери корӣ оғоз мешавад. Ин барои суғурта аз гум шудани бастаи тасдиқ зарур аст. Вақте ки мӯҳлати вақтсанҷ ба охир мерасад, пайвастшавӣ низ қатъ карда мешавад ва захираҳо озод карда мешаванд.

Диаграммаи ҳолати интиқоли боэътимоди UDP

Принсипҳои протокол дар мошини ниҳоии давлатӣ амалӣ карда мешаванд, ки ҳар як ҳолати он барои мантиқи муайяни коркарди бастаҳо масъул аст.
Диаграммаи боэътимоди ҳолати UDP:

Амалисозии протоколи боэътимоди Udp барои .Net

баста — аслан долат нест, он барои автоматдо ибтидо ва инти-хобист. Барои давлат баста блоки идоракунии интиқол қабул карда мешавад, ки сервери асинхронии UDP-ро амалӣ карда, бастаҳоро ба пайвастҳои мувофиқ интиқол медиҳад ва коркарди ҳолатиро оғоз мекунад.

FirstPacketSending – ҳолати ибтидоӣ, ки дар он пайвасти содиротӣ ҳангоми фиристодани паём аст.

Дар ин ҳолат, бастаи аввал барои паёмҳои муқаррарӣ фиристода мешавад. Барои паёмҳои бидуни тасдиқи ирсол, ин ягона ҳолатест, ки тамоми паём фиристода мешавад.

SendingCycle – ҳолати асосӣ барои интиқоли бастаҳои паёмҳо.

Гузариш ба он аз давлат FirstPacketSending пас аз фиристодани бастаи якуми паём анҷом дода мешавад. Маҳз дар ҳамин ҳолат ҳама эътирофҳо ва дархостҳо оид ба интиқоли такрорӣ меоянд. Хуруҷ аз он дар ду ҳолат имконпазир аст - дар сурати бомуваффақият расонидани паём ё бо гузашти вақт.

Аввалин Пакет гирифта шуд – ҳолати ибтидоӣ барои қабулкунандаи паём.

Он дурустии оғози интиқолро месанҷад, сохторҳои заруриро эҷод мекунад ва дар бораи қабули бастаи аввал тасдиқнома мефиристад.

Барои паёме, ки аз як баста иборат аст ва бидуни истифодаи далели интиқол фиристода шудааст, ин ягона ҳолат аст. Пас аз коркарди чунин паём, пайвастшавӣ баста мешавад.

Маҷмӯъ – ҳолати асосӣ барои қабули бастаҳои паёмҳо.

Он бастаҳоро ба нигаҳдории муваққатӣ менависад, гум шудани бастаҳоро месанҷад, барои интиқоли блоки бастаҳо ва тамоми паём изҳорот мефиристад ва дархостҳоро барои интиқоли бастаҳои гумшуда мефиристад. Дар сурати бомуваффақият гирифтани тамоми паём, пайвастшавӣ ба ҳолати худ мегузарад Гузаронида шуд, дар акси ҳол, вақти тамомшавии вақт хориҷ мешавад.

Гузаронида шуд – қатъи пайвастшавӣ дар сурати бомуваффақият гирифтани тамоми паём.

Ин ҳолат барои ҷамъ овардани паём ва дар ҳолате зарур аст, ки тасдиқи интиқоли паём дар роҳ ба ирсолкунанда гум шудааст. Ин ҳолат бо гузашти вақт берун мешавад, аммо пайвастшавӣ бомуваффақият баста ҳисобида мешавад.

Ба код амиқтар. воҳиди идоракунии интиқол

Яке аз унсурҳои асосии UDP Reliable блоки идоракунии интиқол мебошад. Вазифаи ин блок нигоҳ доштани пайвастҳои ҷорӣ ва унсурҳои ёрирасон, тақсимоти бастаҳои воридотӣ ба пайвастҳои мувофиқ, таъмин намудани интерфейс барои фиристодани пакетҳо ба пайвастшавӣ ва татбиқи протоколи API мебошад. Блоки идоракунии интиқол пакетҳоро аз қабати UDP қабул мекунад ва онҳоро барои коркард ба мошини давлатӣ мефиристад. Барои қабули бастаҳо, он сервери асинхронии UDP-ро амалӣ мекунад.
Баъзе аъзоёни синфи ReliableUdpConnectionControlBlock:

internal class ReliableUdpConnectionControlBlock : IDisposable
{
  // массив байт для указанного ключа. Используется для сборки входящих сообщений    
  public ConcurrentDictionary<Tuple<EndPoint, Int32>, byte[]> IncomingStreams { get; private set;}
  // массив байт для указанного ключа. Используется для отправки исходящих сообщений.
  public ConcurrentDictionary<Tuple<EndPoint, Int32>, byte[]> OutcomingStreams { get; private set; }
  // connection record для указанного ключа.
  private readonly ConcurrentDictionary<Tuple<EndPoint, Int32>, ReliableUdpConnectionRecord> m_listOfHandlers;
  // список подписчиков на сообщения.
  private readonly List<ReliableUdpSubscribeObject> m_subscribers;    
  // локальный сокет    
  private Socket m_socketIn;
  // порт для входящих сообщений
  private int m_port;
  // локальный IP адрес
  private IPAddress m_ipAddress;    
  // локальная конечная точка    
  public IPEndPoint LocalEndpoint { get; private set; }    
  // коллекция предварительно инициализированных
  // состояний конечного автомата
  public StatesCollection States { get; private set; }
  // генератор случайных чисел. Используется для создания TransmissionId
  private readonly RNGCryptoServiceProvider m_randomCrypto;    	
  //...
}

Амалисозии сервери асинхронии UDP:

private void Receive()
{
  EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0);
  // создаем новый буфер, для каждого socket.BeginReceiveFrom 
  byte[] buffer = new byte[DefaultMaxPacketSize + ReliableUdpHeader.Length];
  // передаем буфер в качестве параметра для асинхронного метода
  this.m_socketIn.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref connectedClient, EndReceive, buffer);
}   

private void EndReceive(IAsyncResult ar)
{
  EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0);
  int bytesRead = this.m_socketIn.EndReceiveFrom(ar, ref connectedClient);
  //пакет получен, готовы принимать следующий        
  Receive();
  // т.к. простейший способ решить вопрос с буфером - получить ссылку на него 
  // из IAsyncResult.AsyncState        
  byte[] bytes = ((byte[]) ar.AsyncState).Slice(0, bytesRead);
  // получаем заголовок пакета        
  ReliableUdpHeader header;
  if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header))
  {          
    // пришел некорректный пакет - отбрасываем его
    return;
  }
  // конструируем ключ для определения connection record’а для пакета
  Tuple<EndPoint, Int32> key = new Tuple<EndPoint, Int32>(connectedClient, header.TransmissionId);
  // получаем существующую connection record или создаем новую
  ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header.ReliableUdpMessageType));
  // запускаем пакет в обработку в конечный автомат
  record.State.ReceivePacket(record, header, bytes);
}

Барои ҳар як интиқоли паём сохторе сохта мешавад, ки дорои маълумот дар бораи пайвастшавӣ мебошад. Чунин сохтор номида мешавад сабти пайвастшавӣ.
Баъзе аъзоёни синфи ReliableUdpConnectionRecord:

internal class ReliableUdpConnectionRecord : IDisposable
{    
  // массив байт с сообщением    
  public byte[] IncomingStream { get; set; }
  // ссылка на состояние конечного автомата    
  public ReliableUdpState State { get; set; }    
  // пара, однозначно определяющая connection record
  // в блоке управления передачей     
  public Tuple<EndPoint, Int32> Key { get; private set;}
  // нижняя граница приемного окна    
  public int WindowLowerBound;
  // размер окна передачи
  public readonly int WindowSize;     
  // номер пакета для отправки
  public int SndNext;
  // количество пакетов для отправки
  public int NumberOfPackets;
  // номер передачи (именно он и есть вторая часть Tuple)
  // для каждого сообщения свой	
  public readonly Int32 TransmissionId;
  // удаленный IP endpoint – собственно получатель сообщения
  public readonly IPEndPoint RemoteClient;
  // размер пакета, во избежание фрагментации на IP уровне
  // не должен превышать MTU – (IP.Header + UDP.Header + RelaibleUDP.Header)
  public readonly int BufferSize;
  // блок управления передачей
  public readonly ReliableUdpConnectionControlBlock Tcb;
  // инкапсулирует результаты асинхронной операции для BeginSendMessage/EndSendMessage
  public readonly AsyncResultSendMessage AsyncResult;
  // не отправлять пакеты подтверждения
  public bool IsNoAnswerNeeded;
  // последний корректно полученный пакет (всегда устанавливается в наибольший номер)
  public int RcvCurrent;
  // массив с номерами потерянных пакетов
  public int[] LostPackets { get; private set; }
  // пришел ли последний пакет. Используется как bool.
  public int IsLastPacketReceived = 0;
  //...
}

Ба код амиқтар. давлатхо

Давлатҳо мошини давлатии протоколи боэътимоди UDP-ро татбиқ мекунанд, ки дар он коркарди асосии пакетҳо сурат мегирад. Синфи абстрактии ReliableUdpState интерфейсро барои давлат таъмин мекунад:

Амалисозии протоколи боэътимоди Udp барои .Net

Тамоми мантиқи протокол аз ҷониби синфҳои дар боло овардашуда якҷоя бо синфи ёрирасон амалӣ карда мешавад, ки усулҳои статикиро таъмин мекунад, масалан, сохтани сарлавҳаи ReliableUdp аз сабти пайваст.

Минбаъд, мо татбиқи усулҳои интерфейсро, ки алгоритмҳои асосии протоколро муайян мекунанд, ба таври муфассал баррасӣ хоҳем кард.

Усули DisposeByTimeout

Усули DisposeByTimeout барои озод кардани захираҳои пайвастшавӣ пас аз тайм-ут ва сигнали интиқоли бомуваффақият/муваффақияти паём масъул аст.
ReliableUdpState.DisposeByTimeout:

protected virtual void DisposeByTimeout(object record)
{
  ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) record;      
  if (record.AsyncResult != null)
  {
    connectionRecord.AsyncResult.SetAsCompleted(false);
  }
  connectionRecord.Dispose();
}

Он танҳо дар давлат бекор карда шудааст Гузаронида шуд.
Completed.DisposeByTimeout:

protected override void DisposeByTimeout(object record)
{
  ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) record;
  // сообщаем об успешном получении сообщения
  SetAsCompleted(connectionRecord);        
}

Усули ProcessPackets

Усули ProcessPackets барои коркарди иловагии баста ё бастаҳо масъул аст. Бевосита ё тавассути таймери интизории баста даъват карда мешавад.

Дар ҳолати Маҷмӯъ усул аз байн рафтааст ва барои тафтиши бастаҳои гумшуда ва гузаштан ба ҳолат масъул аст Гузаронида шуд, дар сурати гирифтани бастаи охирин ва бомуваффақият гузаштани чек
Assembling.ProcessPackets:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  if (connectionRecord.IsDone != 0)
    return;
  if (!ReliableUdpStateTools.CheckForNoPacketLoss(connectionRecord, connectionRecord.IsLastPacketReceived != 0))
  {
    // есть потерянные пакеты, отсылаем запросы на них
    foreach (int seqNum in connectionRecord.LostPackets)
    {
      if (seqNum != 0)
      {
        ReliableUdpStateTools.SendAskForLostPacket(connectionRecord, seqNum);
      }
    }
    // устанавливаем таймер во второй раз, для повторной попытки передачи
    if (!connectionRecord.TimerSecondTry)
    {
      connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
      connectionRecord.TimerSecondTry = true;
      return;
    }
    // если после двух попыток срабатываний WaitForPacketTimer 
    // не удалось получить пакеты - запускаем таймер завершения соединения
    StartCloseWaitTimer(connectionRecord);
  }
  else if (connectionRecord.IsLastPacketReceived != 0)
  // успешная проверка 
  {
    // высылаем подтверждение о получении блока данных
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
    connectionRecord.State = connectionRecord.Tcb.States.Completed;
    connectionRecord.State.ProcessPackets(connectionRecord);
    // вместо моментальной реализации ресурсов
    // запускаем таймер, на случай, если
    // если последний ack не дойдет до отправителя и он запросит его снова.
    // по срабатыванию таймера - реализуем ресурсы
    // в состоянии Completed метод таймера переопределен
    StartCloseWaitTimer(connectionRecord);
  }
  // это случай, когда ack на блок пакетов был потерян
  else
  {
    if (!connectionRecord.TimerSecondTry)
    {
      ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
      connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
      connectionRecord.TimerSecondTry = true;
      return;
    }
    // запускаем таймер завершения соединения
    StartCloseWaitTimer(connectionRecord);
  }
}

Дар ҳолати SendingCycle ин усул танҳо дар таймер даъват карда мешавад ва барои аз нав фиристодани паёми охирин ва инчунин фаъол кардани таймери бастани пайваст масъул аст.
SendingCycle.ProcessPackets:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  if (connectionRecord.IsDone != 0)
    return;        
  // отправляем повторно последний пакет 
  // ( в случае восстановления соединения узел-приемник заново отправит запросы, которые до него не дошли)        
  ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, connectionRecord.SndNext - 1));
  // включаем таймер CloseWait – для ожидания восстановления соединения или его завершения
  StartCloseWaitTimer(connectionRecord);
}

Дар ҳолати Гузаронида шуд усул таймери коркунандаро қатъ мекунад ва ба муштариён паём мефиристад.
Ба анҷом расид.Пакетҳои коркард:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  if (connectionRecord.WaitForPacketsTimer != null)
    connectionRecord.WaitForPacketsTimer.Dispose();
  // собираем сообщение и передаем его подписчикам
  ReliableUdpStateTools.CreateMessageFromMemoryStream(connectionRecord);
}

Усули ReceivePacket

Дар ҳолати Аввалин Пакет гирифта шуд Вазифаи асосии усул муайян кардани он аст, ки оё бастаи паёми аввал воқеан ба интерфейс омадааст ва инчунин ҷамъоварии паёми иборат аз як бастаи ягона мебошад.
FirstPacketReceived.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket))
    // отбрасываем пакет
    return;
  // комбинация двух флагов - FirstPacket и LastPacket - говорит что у нас единственное сообщение
  if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) &
      header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
  {
    ReliableUdpStateTools.CreateMessageFromSinglePacket(connectionRecord, header, payload.Slice(ReliableUdpHeader.Length, payload.Length));
    if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
    {
      // отправляем пакет подтверждение          
      ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
    }
    SetAsCompleted(connectionRecord);
    return;
  }
  // by design все packet numbers начинаются с 0;
  if (header.PacketNumber != 0)          
    return;
  ReliableUdpStateTools.InitIncomingBytesStorage(connectionRecord, header);
  ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
  // считаем кол-во пакетов, которые должны прийти
  connectionRecord.NumberOfPackets = (int)Math.Ceiling((double) ((double) connectionRecord.IncomingStream.Length/(double) connectionRecord.BufferSize));
  // записываем номер последнего полученного пакета (0)
  connectionRecord.RcvCurrent = header.PacketNumber;
  // после сдвинули окно приема на 1
  connectionRecord.WindowLowerBound++;
  // переключаем состояние
  connectionRecord.State = connectionRecord.Tcb.States.Assembling;
  // если не требуется механизм подтверждение
  // запускаем таймер который высвободит все структуры         
  if (header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
  {
    connectionRecord.CloseWaitTimer = new Timer(DisposeByTimeout, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
  }
  else
  {
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
    connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
  }
}

Дар ҳолати SendingCycle ин усул барои қабули тасдиқи интиқол ва дархостҳои интиқоли такрорӣ бекор карда шудааст.
SendingCycle.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  if (connectionRecord.IsDone != 0)
    return;
  if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.RequestForPacket))
    return;
  // расчет конечной границы окна
  // берется граница окна + 1, для получения подтверждений доставки
  int windowHighestBound = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize), (connectionRecord.NumberOfPackets));
  // проверка на попадание в окно        
  if (header.PacketNumber < connectionRecord.WindowLowerBound || header.PacketNumber > windowHighestBound)
    return;
  connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(-1, -1);
  // проверить на последний пакет:
  if (header.PacketNumber == connectionRecord.NumberOfPackets)
  {
    // передача завершена
    Interlocked.Increment(ref connectionRecord.IsDone);
    SetAsCompleted(connectionRecord);
    return;
  }
  // это ответ на первый пакет c подтверждением         
  if ((header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) && header.PacketNumber == 1))
  {
    // без сдвига окна
    SendPacket(connectionRecord);
  }
  // пришло подтверждение о получении блока данных
  else if (header.PacketNumber == windowHighestBound)
  {
    // сдвигаем окно прием/передачи
    connectionRecord.WindowLowerBound += connectionRecord.WindowSize;
    // обнуляем массив контроля передачи
    connectionRecord.WindowControlArray.Nullify();
    // отправляем блок пакетов
    SendPacket(connectionRecord);
  }
  // это запрос на повторную передачу – отправляем требуемый пакет          
  else
    ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, header.PacketNumber));
}

Дар ҳолати Маҷмӯъ дар усули ReceivePacket кори асосии монтажи паём аз бастаҳои воридотӣ сурат мегирад.
Assembling.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  if (connectionRecord.IsDone != 0)
    return;
  // обработка пакетов с отключенным механизмом подтверждения доставки
  if (header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
  {
    // сбрасываем таймер
    connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1);
    // записываем данные
    ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
    // если получили пакет с последним флагом - делаем завершаем          
    if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
    {
      connectionRecord.State = connectionRecord.Tcb.States.Completed;
      connectionRecord.State.ProcessPackets(connectionRecord);
    }
    return;
  }        
  // расчет конечной границы окна
  int windowHighestBound = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize - 1), (connectionRecord.NumberOfPackets - 1));
  // отбрасываем не попадающие в окно пакеты
  if (header.PacketNumber < connectionRecord.WindowLowerBound || header.PacketNumber > (windowHighestBound))
    return;
  // отбрасываем дубликаты
  if (connectionRecord.WindowControlArray.Contains(header.PacketNumber))
    return;
  // записываем данные 
  ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
  // увеличиваем счетчик пакетов        
  connectionRecord.PacketCounter++;
  // записываем в массив управления окном текущий номер пакета        
  connectionRecord.WindowControlArray[header.PacketNumber - connectionRecord.WindowLowerBound] = header.PacketNumber;
  // устанавливаем наибольший пришедший пакет        
  if (header.PacketNumber > connectionRecord.RcvCurrent)
    connectionRecord.RcvCurrent = header.PacketNumber;
  // перезапускам таймеры        
  connectionRecord.TimerSecondTry = false;
  connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(-1, -1);
  // если пришел последний пакет
  if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
  {
    Interlocked.Increment(ref connectionRecord.IsLastPacketReceived);
  }
  // если нам пришли все пакеты окна, то сбрасываем счетчик
  // и высылаем пакет подтверждение
  else if (connectionRecord.PacketCounter == connectionRecord.WindowSize)
  {
    // сбрасываем счетчик.      
    connectionRecord.PacketCounter = 0;
    // сдвинули окно передачи
    connectionRecord.WindowLowerBound += connectionRecord.WindowSize;
    // обнуление массива управления передачей
    connectionRecord.WindowControlArray.Nullify();
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
  }
  // если последний пакет уже имеется        
  if (Thread.VolatileRead(ref connectionRecord.IsLastPacketReceived) != 0)
  {
    // проверяем пакеты          
    ProcessPackets(connectionRecord);
  }
}

Дар ҳолати Гузаронида шуд вазифаи ягонаи усул фиристодани бори дигар эътирофи бомуваффақият расонидани паём аст.
Ба анҷом расид.Пакетро қабул кунед:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  // повторная отправка последнего пакета в связи с тем,
  // что последний ack не дошел до отправителя
  if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
  {
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
  }
}

Усули ирсоли пакет

Дар ҳолати FirstPacketSending ин усул бастаи якуми маълумотро мефиристад, ё агар паём тасдиқи интиқолро талаб накунад, тамоми паём.
FirstPacketSending.SendPacket:

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{
  connectionRecord.PacketCounter = 0;
  connectionRecord.SndNext = 0;
  connectionRecord.WindowLowerBound = 0;       
  // если подтверждения не требуется - отправляем все пакеты
  // и высвобождаем ресурсы
  if (connectionRecord.IsNoAnswerNeeded)
  {
    // Здесь происходит отправка As Is
    do
    {
      ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, ReliableUdpStateTools. CreateReliableUdpHeader(connectionRecord)));
      connectionRecord.SndNext++;
    } while (connectionRecord.SndNext < connectionRecord.NumberOfPackets);
    SetAsCompleted(connectionRecord);
    return;
  }
  // создаем заголовок пакета и отправляем его 
  ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
  ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));
  // увеличиваем счетчик
  connectionRecord.SndNext++;
  // сдвигаем окно
  connectionRecord.WindowLowerBound++;
  connectionRecord.State = connectionRecord.Tcb.States.SendingCycle;
  // Запускаем таймер
  connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
}

Дар ҳолати SendingCycle дар ин усул блоки пакетҳо фиристода мешавад.
SendingCycle.SendPacket:

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{      
  // отправляем блок пакетов      
  for (connectionRecord.PacketCounter = 0;
        connectionRecord.PacketCounter < connectionRecord.WindowSize &&
        connectionRecord.SndNext < connectionRecord.NumberOfPackets;
        connectionRecord.PacketCounter++)
  {
    ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
    ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));
    connectionRecord.SndNext++;
  }
  // на случай большого окна передачи, перезапускаем таймер после отправки
  connectionRecord.WaitForPacketsTimer.Change( connectionRecord.ShortTimerPeriod, -1 );
  if ( connectionRecord.CloseWaitTimer != null )
  {
    connectionRecord.CloseWaitTimer.Change( -1, -1 );
  }
}

Ба код амиқтар. Эҷод ва таъсиси робитаҳо

Акнун, ки мо ҳолатҳои асосӣ ва усулҳоеро, ки барои коркарди давлатҳо истифода мешаванд, дидем, биёед чанд мисолеро, ки чӣ тавр протокол кор мекунад, каме муфассалтар ҷудо кунем.
Диаграммаи интиқоли маълумот дар шароити муқаррарӣ:Амалисозии протоколи боэътимоди Udp барои .Net

Эҷодро ба таври муфассал баррасӣ кунед сабти пайвастшавӣ барои пайваст кардан ва фиристодани бастаи аввал. Интиқол ҳамеша аз ҷониби барномае оғоз мешавад, ки API паёми ирсолро даъват мекунад. Баъдан, усули StartTransmission блоки идоракунии интиқол даъват карда мешавад, ки интиқоли маълумотро барои паёми нав оғоз мекунад.
Эҷоди пайвасти баромад:

private void StartTransmission(ReliableUdpMessage reliableUdpMessage, EndPoint endPoint, AsyncResultSendMessage asyncResult)
{
  if (m_isListenerStarted == 0)
  {
    if (this.LocalEndpoint == null)
    {
      throw new ArgumentNullException( "", "You must use constructor with parameters or start listener before sending message" );
    }
    // запускаем обработку входящих пакетов
    StartListener(LocalEndpoint);
  }
  // создаем ключ для словаря, на основе EndPoint и ReliableUdpHeader.TransmissionId        
  byte[] transmissionId = new byte[4];
  // создаем случайный номер transmissionId        
  m_randomCrypto.GetBytes(transmissionId);
  Tuple<EndPoint, Int32> key = new Tuple<EndPoint, Int32>(endPoint, BitConverter.ToInt32(transmissionId, 0));
  // создаем новую запись для соединения и проверяем, 
  // существует ли уже такой номер в наших словарях
  if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, reliableUdpMessage, asyncResult)))
  {
    // если существует – то повторно генерируем случайный номер 
    m_randomCrypto.GetBytes(transmissionId);
    key = new Tuple<EndPoint, Int32>(endPoint, BitConverter.ToInt32(transmissionId, 0));
    if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, reliableUdpMessage, asyncResult)))
      // если снова не удалось – генерируем исключение
      throw new ArgumentException("Pair TransmissionId & EndPoint is already exists in the dictionary");
  }
  // запустили состояние в обработку         
  m_listOfHandlers[key].State.SendPacket(m_listOfHandlers[key]);
}

Фиристодани бастаи аввал (Ҳолати FirstPacketSending):

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{
  connectionRecord.PacketCounter = 0;
  connectionRecord.SndNext = 0;
  connectionRecord.WindowLowerBound = 0;       
  // ... 
  // создаем заголовок пакета и отправляем его 
  ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
  ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));
  // увеличиваем счетчик
  connectionRecord.SndNext++;
  // сдвигаем окно
  connectionRecord.WindowLowerBound++;
  // переходим в состояние SendingCycle
  connectionRecord.State = connectionRecord.Tcb.States.SendingCycle;
  // Запускаем таймер
  connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
}

Пас аз фиристодани бастаи аввал, ирсолкунанда ба ҳолат ворид мешавад SendingCycle – тасдиқи интиқоли бастаро интизор шавед.
Ҷониби қабулкунанда бо истифода аз усули EndReceive бастаи ирсолшударо қабул мекунад ва нав месозад сабти пайвастшавӣ ва ин бастаро бо сарлавҳаи қаблан таҳлилшуда ба усули ReceivePacket-и давлат барои коркард мегузаронад Аввалин Пакет гирифта шуд
Эҷоди пайвастшавӣ дар тарафи қабулкунанда:

private void EndReceive(IAsyncResult ar)
{
  // ...
  // пакет получен
  // парсим заголовок пакета        
  ReliableUdpHeader header;
  if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header))
  {          
    // пришел некорректный пакет - отбрасываем его
    return;
  }
  // конструируем ключ для определения connection record’а для пакета
  Tuple<EndPoint, Int32> key = new Tuple<EndPoint, Int32>(connectedClient, header.TransmissionId);
  // получаем существующую connection record или создаем новую
  ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header. ReliableUdpMessageType));
  // запускаем пакет в обработку в конечный автомат
  record.State.ReceivePacket(record, header, bytes);
}

Гирифтани бастаи аввал ва фиристодани тасдиқ (Ҳолати FirstPacketReceived):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket))
    // отбрасываем пакет
    return;
  // ...
  // by design все packet numbers начинаются с 0;
  if (header.PacketNumber != 0)          
    return;
  // инициализируем массив для хранения частей сообщения
  ReliableUdpStateTools.InitIncomingBytesStorage(connectionRecord, header);
  // записываем данные пакет в массив
  ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
  // считаем кол-во пакетов, которые должны прийти
  connectionRecord.NumberOfPackets = (int)Math.Ceiling((double) ((double) connectionRecord.IncomingStream.Length/(double) connectionRecord.BufferSize));
  // записываем номер последнего полученного пакета (0)
  connectionRecord.RcvCurrent = header.PacketNumber;
  // после сдвинули окно приема на 1
  connectionRecord.WindowLowerBound++;
  // переключаем состояние
  connectionRecord.State = connectionRecord.Tcb.States.Assembling;  
  if (/*если не требуется механизм подтверждение*/)
  // ...
  else
  {
    // отправляем подтверждение
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
    connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
  }
}

Ба код амиқтар. Бастани пайвастшавӣ дар вақти тамомшавӣ

Муносибати мӯҳлат як қисми муҳими UDP боэътимод аст. Мисоли онро дида бароед, ки дар он гиреҳи мобайнӣ ноком шуд ва интиқоли маълумот дар ҳарду самт ғайриимкон шуд.
Диаграммаи бастани пайвастшавӣ бо вақти фаромадан:Амалисозии протоколи боэътимоди Udp барои .Net

Тавре ки аз диаграмма дида мешавад, таймери кории ирсолкунанда фавран пас аз фиристодани блоки пакетҳо оғоз меёбад. Ин дар усули SendPacket-и давлат рӯй медиҳад SendingCycle.
Фаъол кардани таймери корӣ (ҳолати SendingCycle):

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{      
  // отправляем блок пакетов   
  // ...   
  // перезапускаем таймер после отправки
  connectionRecord.WaitForPacketsTimer.Change( connectionRecord.ShortTimerPeriod, -1 );
  if ( connectionRecord.CloseWaitTimer != null )
    connectionRecord.CloseWaitTimer.Change( -1, -1 );
}

Давраҳои вақтсанҷ ҳангоми эҷоди пайваст муқаррар карда мешаванд. ShortTimerPeriod пешфарз 5 сония аст. Дар мисол, он ба 1,5 сония муқаррар карда шудааст.

Барои пайвасти воридотӣ, вақтсанҷ пас аз қабули охирин бастаи додаҳои воридотӣ оғоз меёбад, ин дар усули ReceivePacket-и ҳолат рӯй медиҳад. Маҷмӯъ
Фаъол кардани таймери корӣ (Ҳолати васлкунӣ):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  // ... 
  // перезапускаем таймеры        
  connectionRecord.TimerSecondTry = false;
  connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(-1, -1);
  // ...
}

Ҳангоми интизории таймери корӣ ба пайвасти воридотӣ дигар бастаҳо нарасиданд. Таймер хомӯш шуд ва усули ProcessPackets-ро даъват кард, ки дар он ҷо бастаҳои гумшуда пайдо шуданд ва бори аввал дархостҳои интиқол фиристода шуданд.
Ирсоли дархостҳои интиқоли дубора (Ҳолати васлшавӣ):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  // ...        
  if (/*проверка на потерянные пакеты */)
  {
    // отправляем запросы на повторную доставку
    // устанавливаем таймер во второй раз, для повторной попытки передачи
    if (!connectionRecord.TimerSecondTry)
    {
      connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
    connectionRecord.TimerSecondTry = true;
    return;
    }
  // если после двух попыток срабатываний WaitForPacketTimer 
  // не удалось получить пакеты - запускаем таймер завершения соединения
  StartCloseWaitTimer(connectionRecord);
  }
  else if (/*пришел последний пакет и успешная проверка */)
  {
    // ...
    StartCloseWaitTimer(connectionRecord);
  }
  // если ack на блок пакетов был потерян
  else
  { 
    if (!connectionRecord.TimerSecondTry)
    {
      // повторно отсылаем ack
      connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
      connectionRecord.TimerSecondTry = true;
      return;
    }
    // запускаем таймер завершения соединения
    StartCloseWaitTimer(connectionRecord);
  }
}

Тағйирёбандаи TimerSecondTry ба танзим гузошта шудааст ҳақиқӣ. Ин тағирёбанда барои аз нав оғоз кардани таймери корӣ масъул аст.

Аз ҷониби ирсолкунанда, таймери корӣ низ ба кор андохта мешавад ва бастаи охирини ирсолшуда дубора фиристода мешавад.
Вақтсанҷи бастани пайвастшавӣ фаъол карда мешавад (ҳолати SendingCycle):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  // ...        
  // отправляем повторно последний пакет 
  // ...        
  // включаем таймер CloseWait – для ожидания восстановления соединения или его завершения
  StartCloseWaitTimer(connectionRecord);
}

Пас аз он, таймери бастани пайвастшавӣ дар пайвасти баромад оғоз меёбад.
ReliableUdpState.StartCloseWaitTimer:

protected void StartCloseWaitTimer(ReliableUdpConnectionRecord connectionRecord)
{
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1);
  else
    connectionRecord.CloseWaitTimer = new Timer(DisposeByTimeout, connectionRecord, connectionRecord.LongTimerPeriod, -1);
}

Давраи қатъи таймери пайвастшавӣ бо нобаёнӣ 30 сония аст.

Пас аз як муддати кӯтоҳ, таймери корӣ дар паҳлӯи гиранда боз оташ мегирад, дархостҳо дубора фиристода мешаванд ва пас аз он таймери бастани пайвастшавӣ барои пайвасти воридотӣ оғоз мешавад.

Вақте ки таймерҳои наздик оташ мегиранд, тамоми захираҳои ҳарду сабти пайвастшавӣ озод мешаванд. Ирсолкунанда дар бораи нокомии интиқол ба барномаи болоӣ (API-и боэътимоди UDP нигаред).
Интишори захираҳои сабти пайвастшавӣ:

public void Dispose()
{
  try
  {
    System.Threading.Monitor.Enter(this.LockerReceive);
  }
  finally
  {
    Interlocked.Increment(ref this.IsDone);
    if (WaitForPacketsTimer != null)
    {
      WaitForPacketsTimer.Dispose();
    }
    if (CloseWaitTimer != null)
    {
      CloseWaitTimer.Dispose();
    }
    byte[] stream;
    Tcb.IncomingStreams.TryRemove(Key, out stream);
    stream = null;
    Tcb.OutcomingStreams.TryRemove(Key, out stream);
    stream = null;
    System.Threading.Monitor.Exit(this.LockerReceive);
  }
}

Ба код амиқтар. Барқарор кардани интиқоли маълумот

Диаграммаи барқарорсозии интиқоли маълумот дар ҳолати гум шудани баста:Амалисозии протоколи боэътимоди Udp барои .Net

Тавре ки аллакай дар бастани пайвастшавӣ дар вақти таймер баррасӣ шуда буд, вақте ки мӯҳлати таймери корӣ ба охир мерасад, қабулкунанда бастаҳои гумшударо тафтиш мекунад. Дар сурати гум шудани бастаҳо, рӯйхати шумораи бастаҳое, ки ба қабулкунанда нарасидаанд, тартиб дода мешавад. Ин рақамҳо ба массиви LostPackets пайвасти мушаххас ворид карда мешаванд ва дархостҳо барои интиқол фиристода мешаванд.
Фиристодани дархостҳо барои интиқоли бастаҳо (Ҳолати васлкунӣ):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
  //...
  if (!ReliableUdpStateTools.CheckForNoPacketLoss(connectionRecord, connectionRecord.IsLastPacketReceived != 0))
  {
    // есть потерянные пакеты, отсылаем запросы на них
    foreach (int seqNum in connectionRecord.LostPackets)
    {
      if (seqNum != 0)
      {
        ReliableUdpStateTools.SendAskForLostPacket(connectionRecord, seqNum);
      }
    }
    // ...
  }
}

Ирсолкунанда дархости интиқолро қабул мекунад ва бастаҳои гумшударо мефиристад. Қобили зикр аст, ки дар айни замон ирсолкунанда таймери бастани пайвастро оғоз кардааст ва ҳангоми қабули дархост он аз нав барқарор карда мешавад.
Аз нав фиристодани бастаҳои гумшуда (ҳолати SendingCycle):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  // ...
  connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
  // сброс таймера закрытия соединения 
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(-1, -1);
  // ...
  // это запрос на повторную передачу – отправляем требуемый пакет          
  else
    ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, header.PacketNumber));
}

Бастаи фиристодашуда (бастаи №3 дар диаграмма) тавассути пайвасти воридотӣ қабул карда мешавад. Барои дидани он, ки равзанаи қабул пур аст ва интиқоли муқаррарии маълумот барқарор шудааст, тафтиш карда мешавад.
Санҷиши хитҳо дар равзанаи қабул (Ҳолати васлкунӣ):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
  // ...
  // увеличиваем счетчик пакетов        
  connectionRecord.PacketCounter++;
  // записываем в массив управления окном текущий номер пакета        
  connectionRecord.WindowControlArray[header.PacketNumber - connectionRecord.WindowLowerBound] = header.PacketNumber;
  // устанавливаем наибольший пришедший пакет        
  if (header.PacketNumber > connectionRecord.RcvCurrent)
    connectionRecord.RcvCurrent = header.PacketNumber;
  // перезапускам таймеры        
  connectionRecord.TimerSecondTry = false;
  connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(-1, -1);
  // ...
  // если нам пришли все пакеты окна, то сбрасываем счетчик
  // и высылаем пакет подтверждение
  else if (connectionRecord.PacketCounter == connectionRecord.WindowSize)
  {
    // сбрасываем счетчик.      
    connectionRecord.PacketCounter = 0;
    // сдвинули окно передачи
    connectionRecord.WindowLowerBound += connectionRecord.WindowSize;
    // обнуление массива управления передачей
    connectionRecord.WindowControlArray.Nullify();
    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
  }
  // ...
}

API-и боэътимоди UDP

Барои муошират бо протоколи интиқоли додаҳо, синфи кушодаи Reliable Udp мавҷуд аст, ки бастабандии блоки идоракунии интиқол мебошад. Дар ин ҷо муҳимтарин аъзоёни синф ҳастанд:

public sealed class ReliableUdp : IDisposable
{
  // получает локальную конечную точку
  public IPEndPoint LocalEndpoint    
  // создает экземпляр ReliableUdp и запускает
  // прослушивание входящих пакетов на указанном IP адресе
  // и порту. Значение 0 для порта означает использование
  // динамически выделенного порта
  public ReliableUdp(IPAddress localAddress, int port = 0) 
  // подписка на получение входящих сообщений
  public ReliableUdpSubscribeObject SubscribeOnMessages(ReliableUdpMessageCallback callback, ReliableUdpMessageTypes messageType = ReliableUdpMessageTypes.Any, IPEndPoint ipEndPoint = null)    
  // отписка от получения сообщений
  public void Unsubscribe(ReliableUdpSubscribeObject subscribeObject)
  // асинхронно отправить сообщение 
  // Примечание: совместимость с XP и Server 2003 не теряется, т.к. используется .NET Framework 4.0
  public Task<bool> SendMessageAsync(ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteEndPoint, CancellationToken cToken)
  // начать асинхронную отправку сообщения
  public IAsyncResult BeginSendMessage(ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteEndPoint, AsyncCallback asyncCallback, Object state)
  // получить результат асинхронной отправки
  public bool EndSendMessage(IAsyncResult asyncResult)  
  // очистить ресурсы
  public void Dispose()    
}

Паёмҳо тавассути обуна қабул карда мешаванд. Имзои вакил барои усули бозгашт:

public delegate void ReliableUdpMessageCallback( ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteClient );

Message:

public class ReliableUdpMessage
{
  // тип сообщения, простое перечисление
  public ReliableUdpMessageTypes Type { get; private set; }
  // данные сообщения
  public byte[] Body { get; private set; }
  // если установлено в true – механизм подтверждения доставки будет отключен
  // для передачи конкретного сообщения
  public bool NoAsk { get; private set; }
}

Барои обуна ба навъи паёми мушаххас ва/ё ирсолкунандаи мушаххас, ду параметри ихтиёрӣ истифода мешаванд: ReliableUdpMessageTypes messageType ва IPEndPoint ipEndPoint.

Намудҳои паёмҳо:

public enum ReliableUdpMessageTypes : short
{ 
  // Любое
  Any = 0,
  // Запрос к STUN server 
  StunRequest = 1,
  // Ответ от STUN server
  StunResponse = 2,
  // Передача файла
  FileTransfer =3,
  // ...
}

Паём ба таври асинхронӣ фиристода мешавад; барои ин, протокол модели барномасозии асинхронӣ амалӣ мекунад:

public IAsyncResult BeginSendMessage(ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteEndPoint, AsyncCallback asyncCallback, Object state)

Натиҷаи фиристодани паём ҳақиқӣ хоҳад буд - агар паём ба қабулкунанда бомуваффақият расида бошад ва нодуруст бошад - агар пайвастшавӣ бо вақти тамомшавӣ баста шуда бошад:

public bool EndSendMessage(IAsyncResult asyncResult)

хулоса

Дар ин мақола бисёр чизҳо тавсиф карда нашудаанд. Механизмҳои мувофиқати ришта, истисно ва коркарди хатогиҳо, татбиқи усулҳои ирсоли паёмҳои асинхронӣ. Аммо асосии протокол, тавсифи мантиқи коркарди бастаҳо, барқарор кардани пайвастшавӣ ва коркарди вақтҳо бояд барои шумо равшан бошад.

Варианти намоишии протоколи таҳвили боэътимод барои қонеъ кардани талаботҳои қаблан муайяншуда устувор ва чандир аст. Аммо ман мехоҳам илова кунам, ки татбиқи тавсифшуда метавонад такмил дода шавад. Масалан, барои баланд бардоштани ҳосилнокӣ ва ба таври динамикӣ тағир додани давраҳои таймер, метавон ба протокол механизмҳоро ба монанди равзанаи лағжиш ва RTT илова кард, инчунин татбиқи механизми муайян кардани MTU байни гиреҳҳои пайвастшавӣ муфид хоҳад буд (вале танҳо агар паёмҳои калон фиристода шаванд) .

Ташаккур барои таваҷҷуҳ, ман интизори шарҳҳо ва шарҳҳои шумо.

PS Барои онҳое, ки ба тафсилот таваҷҷӯҳ доранд ё танҳо мехоҳанд протоколро санҷанд, истиноди лоиҳа дар GitHube:
Лоиҳаи боэътимоди UDP

Истинодҳо ва мақолаҳои муфид

  1. Мушаххасоти протоколи TCP: бо забони англисӣ и бо забони русӣ
  2. Мушаххасоти протоколи UDP: бо забони англисӣ и бо забони русӣ
  3. Муҳокимаи протоколи RUDP: лоиҳаи-ietf-sigtran-reliable-udp-00
  4. Протоколи боэътимоди маълумот: RF 908 и RF 1151
  5. Татбиқи оддии тасдиқи интиқол тавассути UDP: Назорати пурраи шабакаҳои худро бо .NET ва UDP гиред
  6. Мақолаи тавсифи механизмҳои гузариш NAT: Муоширати ҳамсол ба ҳамсол дар саросари тарҷумонҳои суроғаи шабака
  7. Амалисозии модели барномасозии асинхронӣ: Татбиқи модели барномасозии асинхронии CLR и Тарзи тарроҳии IAsyncResult -ро чӣ гуна бояд татбиқ кард
  8. Гузаронидани модели барномасозии асинхронӣ ба намунаи асинхронии ба вазифа асосёфта (APM дар TAP):
    TPL ва анъанавии .NET барномасозии асинхронӣ
    Ҳамкорӣ бо дигар намунаҳо ва намудҳои асинхронӣ

Навсозӣ: Ташаккур майоровп и сидрисҷ барои идеяи илова кардани вазифа ба интерфейс. Мутобиқати китобхона бо системаҳои амалиётии кӯҳна вайрон карда намешавад, зеро Чаҳорчӯбаи 4-ум ҳам сервери XP ва 2003-ро дастгирӣ мекунад.

Манбаъ: will.com

Илова Эзоҳ