Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

I-inthanethi ishintshile kudala. Enye yezimiso eziyinhloko ze-intanethi - i-UDP isetshenziswa izinhlelo zokusebenza hhayi kuphela ukuletha ama-datagrams nokusakaza, kodwa futhi ukunikeza ukuxhumana "koontanga-kuya-ntanga" phakathi kwamanodi enethiwekhi. Ngenxa yokuklama kwayo okulula, le protocol inokusetshenziswa okuningi okungahleliwe ngaphambili, noma kunjalo, ukushiyeka kwephrothokholi, njengokuntuleka kokulethwa okuqinisekisiwe, akuzange kunyamalale noma kuphi. Lesi sihloko sichaza ukuqaliswa kwephrothokholi yokulethwa okuqinisekisiwe nge-UDP.
Okuqukethwe:entry
Izidingo zephrothokholi
Isihloko esithembekile se-UDP
Izimiso ezijwayelekile zephrothokholi
Isikhathi sokuvala kanye nezibali sikhathi zephrothokholi
Umdwebo wesimo sokudlulisa we-UDP esithembekile
Kujule kukhodi. iyunithi yokulawula ukudluliswa
Kujule kukhodi. uthi

Kujule kukhodi. Ukudala nokusungula Izixhumanisi
Kujule kukhodi. Ukuvala ukuxhumeka ngesikhathi sokuvala
Kujule kukhodi. Ibuyisela ukudluliswa kwedatha
I-UDP API ethembekile
isiphetho
Izixhumanisi eziwusizo nezindatshana

entry

Isakhiwo sokuqala se-inthanethi sibonisa indawo yamakheli efanayo lapho inodi ngayinye inekheli le-IP lomhlaba jikelele nelihlukile futhi ikwazi ukuxhumana ngokuqondile namanye ama-node. Manje i-inthanethi, empeleni, inokwakheka okuhlukile - indawo eyodwa yamakheli e-IP womhlaba kanye nezindawo eziningi ezinamakheli ayimfihlo afihlwe ngemuva kwemishini ye-NAT.Kulesi sakhiwo, amadivayisi asendaweni yamakheli kuphela angaxhumana kalula nanoma ubani okunethiwekhi ngenxa yokuthi anekheli le-IP eliyingqayizivele, elishintshekayo emhlabeni jikelele. I-node etholakala kunethiwekhi yangasese ingaxhuma kwamanye ama-node kunethiwekhi efanayo, futhi ixhume kwamanye, ama-node awaziwayo endaweni yamakheli omhlaba. Lokhu kusebenzisana kufinyelelwa kakhulu ngenxa yendlela yokuhumusha ikheli lenethiwekhi. Amadivayisi e-NAT, njengamarutha e-Wi-Fi, adala okufakiwe kwethebula lokuhumusha okukhethekile kokuxhumana okuphumayo futhi alungise amakheli e-IP nezinombolo zembobo emaphaketheni. Lokhu kuvumela ukuxhumana okuphumayo ukuthi kwenziwe kusuka kunethiwekhi yangasese kuya kubasingathi endaweni yamakheli omhlaba. Kodwa ngesikhathi esifanayo, amadivaysi e-NAT ngokuvamile avimbela yonke ithrafikhi engenayo ngaphandle uma kunemithetho ehlukene ebekiwe yokuxhumana okungenayo.

Lesi sakhiwo se-inthanethi silungile ngokwanele ekuhlanganyeleni kweklayenti-neseva, lapho amaklayenti angatholakala kumanethiwekhi ayimfihlo, futhi amaseva anekheli lomhlaba jikelele. Kodwa kudala ubunzima bokuxhumanisa ama-node amabili phakathi ezihlukahlukene amanethiwekhi ayimfihlo. Ukuxhumana okuqondile phakathi kwamanodi amabili kubalulekile ezinhlelweni zokusebenza zontanga kuya kontanga ezifana nokudluliswa kwezwi (Skype), ukuthola ukufinyelela okukude kukhompyutha (TeamViewer), noma imidlalo ye-inthanethi.

Enye yezindlela eziphumelela kakhulu zokusungula ukuxhumana kontanga phakathi kwamadivayisi atholakala kumanethiwekhi angasese ahlukene ibizwa ngokuthi “ukubhoboza imbobo”. Le nqubo ivame ukusetshenziswa kakhulu nezinhlelo zokusebenza ezisuselwe kuphrothokholi ye-UDP.

Kodwa uma uhlelo lwakho lokusebenza ludinga ukulethwa okuqinisekisiwe kwedatha, isibonelo, udlulisa amafayela phakathi kwamakhompyutha, bese usebenzisa i-UDP kuzoba nobunzima obuningi ngenxa yokuthi i-UDP ayiyona iphrothokholi yokulethwa okuqinisekisiwe futhi ayihlinzeki ngokulethwa kwephakethe ngokulandelana, ngokungafani ne-TCP. Iphrothokholi.

Kulokhu, ukuze kuqinisekiswe ukulethwa okuqinisekisiwe kwamaphakethe, kuyadingeka ukusebenzisa iphrothokholi yezinga lesicelo ehlinzeka ngokusebenza okudingekayo futhi isebenze ngaphezulu kwe-UDP.

Ngifuna ukuqaphela ngaso leso sikhathi ukuthi kunendlela ye-TCP yokubhoboza imbobo yokusungula ukuxhumana kwe-TCP phakathi kwama-node kumanethiwekhi angasese ahlukene, kodwa ngenxa yokuntula ukusekelwa kwayo ngamadivaysi amaningi e-NAT, ngokuvamile ayibhekwa njengendlela eyinhloko yokuxhuma. ama-node anjalo.

Esikhathini esisele salesi sihloko, ngizogxila kuphela ekusetshenzisweni kwephrothokholi yokulethwa okuqinisekisiwe. Ukuqaliswa kwesu le-UDP lokubhoboza imbobo kuzochazwa ezihlokweni ezilandelayo.

Izidingo zephrothokholi

  1. Ukulethwa kwephakethe okuthembekile kwenziwa ngendlela yempendulo eyakhayo (lokho okubizwa ngokuthi ukuvuma okuhle)
  2. Isidingo sokudluliswa okuphumelelayo kwedatha enkulu, i.e. iphrothokholi kumele igweme ukudlulisa iphakethe okungadingekile
  3. Kufanele kwenzeke ukukhansela indlela yokuqinisekisa ukulethwa (ikhono lokusebenza njengephrothokholi "ehlanzekile" ye-UDP)
  4. Ikhono lokusebenzisa imodi yomyalo, ngokuqinisekisa umlayezo ngamunye
  5. Iyunithi eyisisekelo yokudluliswa kwedatha ngephrothokholi kufanele kube umlayezo

Lezi zidingo zihambisana kakhulu nezimfuneko ze-Reliable Data Protocol ezichazwe ku rfc 908 и rfc 1151, futhi ngathembela kulawo mazinga lapho ngithuthukisa le nqubo yomthetho.

Ukuze siqonde lezi zidingo, ake sibheke imidwebo yesikhathi yokudluliswa kwedatha phakathi kwamanodi enethiwekhi amabili kusetshenziswa izivumelwano ze-TCP ne-UDP. Masilahlekelwe iphakethe elilodwa kuzo zombili izimo.
Ukudluliswa kwedatha engasebenzisani nge-TCP:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Njengoba ungabona kumdwebo, uma kwenzeka ukulahleka kwephakethe, i-TCP izobona iphakethe elilahlekile futhi ibike kumthumeli ngokucela inombolo yengxenye elahlekile.
Ukudluliswa kwedatha ngephrothokholi ye-UDP:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

I-UDP ayithathi zinyathelo zokuthola ukulahlekelwa. Ukulawulwa kwamaphutha okudlulisela kuphrothokholi ye-UDP kuwumthwalo wesicelo ngokuphelele.

Ukutholwa kwephutha kuphrothokholi ye-TCP kufinyelelwa ngokusungula uxhumano ngendawo yokugcina, ukugcina isimo salokho kuxhumana, okubonisa inani lamabhayithi athunyelwe kusihloko sephakethe ngalinye, nokwazisa amarisidi kusetshenziswa inombolo yokuvuma.

Ukwengeza, ukuze kuthuthukiswe ukusebenza (okungukuthi, ukuthumela ingxenye engaphezu kweyodwa ngaphandle kokuthola ukuvunywa), iphrothokholi ye-TCP isebenzisa lokho okubizwa ngokuthi iwindi lokudlulisela - inani lamabhayithi edatha umthumeli wengxenye alindele ukuyithola.

Ukuze uthole ulwazi olwengeziwe mayelana nephrothokholi ye-TCP, bheka rfc 793, kusuka ku-UDP kuya rfc 768lapho, empeleni, zichazwa.

Kusukela kulokhu okungenhla, kuyacaca ukuthi ukuze udale iphrothokholi ethembekile yokulethwa komlayezo nge-UDP (ngemuva kwalokhu sizobiza I-UDP ethembekile), kuyadingeka ukusebenzisa izindlela zokudlulisa idatha ezifana ne-TCP. Okungukuthi:

  • londoloza isimo sokuxhuma
  • sebenzisa izinombolo zesegimenti
  • sebenzisa amaphakheji okuqinisekisa akhethekile
  • sebenzisa indlela yewindi eyenziwe lula ukuze ukhuphule ukuphuma kwephrothokholi

Ukwengeza, udinga:

  • khombisa ukuqala komlayezo, ukwaba izinsiza zokuxhuma
  • bonisa ukuphela komlayezo, ukudlulisa umlayezo owamukelwe kuhlelo lokusebenza olukhuphuka nomfula futhi ukhulule izinsiza zephrothokholi
  • vumela iphrothokholi eqondene nokuxhumeka ukuthi ikhubaze indlela yokuqinisekisa ukulethwa ukuthi isebenze njenge-UDP "emsulwa".

Isihloko esithembekile se-UDP

Khumbula ukuthi idathagram ye-UDP ifakwe kudathagram ye-IP. Iphakethe le-UDP Ethembekile "lisongwe" ngokufanelekile kudathagram ye-UDP.
Ukufakwa kwesihloko okuthembekile kwe-UDP:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Isakhiwo sesihloko esinokwethenjelwa se-UDP silula kakhulu:

Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

  • Amafulegi - amafulegi okulawula iphakheji
  • I-MessageType - uhlobo lomlayezo olusetshenziswa izinhlelo zokusebenza ezikhuphukayo ukuze ubhalisele imilayezo ethile
  • I-TransmissionId - inombolo yokudlulisela, kanye nekheli kanye nechweba lomamukeli, ikhomba ngokuhlukile uxhumano
  • PacketNumber - inombolo yephakethe
  • Izinketho - izinketho ezengeziwe zephrothokholi. Endabeni yephakethe lokuqala, lisetshenziselwa ukukhombisa usayizi womlayezo

Amafulegi ami kanje:

  • FirstPacket - iphakethe lokuqala lomlayezo
  • I-NoAsk - umlayezo awudingi indlela yokuvuma ukuthi ivunyelwe
  • I-LastPacket - iphakethe lomlayezo wokugcina
  • I-RequestForPacket - iphakethe lokuqinisekisa noma isicelo sephakethe elilahlekile

Izimiso ezijwayelekile zephrothokholi

Njengoba i-UDP Ethembekile igxile ekudluliselweni komlayezo okuqinisekisiwe phakathi kwama-node amabili, kufanele ikwazi ukusungula ukuxhumana nolunye uhlangothi. Ukusungula uxhumano, umthumeli uthumela iphakethe elinefulegi le-FirstPacket, impendulo kuyosho ukuthi uxhumano lusunguliwe. Wonke amaphakethe okuphendula, noma, ngamanye amazwi, amaphakethe okuvuma, ahlale ebeka inani lenkambu ye-PacketNumber libe ngaphezu kwenani elikhulu le-PacketNumber lamaphakethe atholwe ngempumelelo. Indawo Yezinketho yephakethe lokuqala elithunyelwe ngusayizi womyalezo.

Indlela efanayo isetshenziswa ukunqamula uxhumano. Ifulegi le-LastPacket lisethwe ephaketheni lokugcina lomlayezo. Iphakethe lempendulo liqukethe inombolo yephakethe lokugcina + 1, okuthi ohlangothini lokwamukelayo kusho ukuthunyelwa ngempumelelo komlayezo.
Umdwebo wokusungulwa nokuqedwa kokuxhumana:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Lapho uxhumano selusungulwe, ukudluliswa kwedatha kuyaqala. Idatha idluliselwa ngamabhulokhi amaphakethe. Ibhulokhi ngayinye, ngaphandle kweyokugcina, iqukethe inani elimisiwe lamaphakethe. Ilingana nosayizi wewindi lokwamukela/lokudlulisa. Ibhulokhi yedatha yokugcina ingaba namaphakethe ambalwa. Ngemva kokuthumela ibhulokhi ngayinye, uhlangothi oluthumelayo lulinda ukuqinisekiswa kokulethwa, noma isicelo sokulethwa kabusha kwamaphakethe alahlekile, okushiya iwindi lokwamukela/lokudlulisela livuliwe ukuze lithole izimpendulo. Ngemva kokuthola isiqinisekiso sokulethwa kwebhulokhi, iwindi lokwamukela/lokudlulisela liyashintsha bese kuthunyelwa ibhulokhi elandelayo yedatha.

Uhlangothi olwamukelayo lwamukela amaphakethe. Iphakethe ngalinye liyahlolwa ukuze kubonakale ukuthi liwela ngaphakathi kwefasitela lokudlulisela. Amaphakethe nezimpinda ezingaweli efasiteleni ziyahlungwa. Ngoba Uma usayizi wefasitela ulungisiwe futhi ufana kumamukeli kanye nomthumeli, khona-ke esimweni lapho ibhulokhi yamaphakethe ilethwa ngaphandle kokulahlekelwa, iwindi liyashintshwa ukuze lithole amaphakethe ebhulokhi elandelayo yedatha kanye nokuqinisekiswa kokulethwa. ithunyelwe. Uma iwindi lingagcwali phakathi nesikhathi esibekwe isibali-sikhathi somsebenzi, kuzobe sekuqala isheke lapho amaphakethe engakalethwanga futhi izicelo zokulethwa kabusha zizothunyelwa.
Umdwebo wokudlulisela kabusha:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Isikhathi sokuvala kanye nezibali sikhathi zephrothokholi

Kunezizathu ezimbalwa zokuthi kungani uxhumano lungasungulwa. Isibonelo, uma iqembu elitholayo lingaxhunyiwe ku-inthanethi. Kulokhu, lapho uzama ukusungula uxhumano, uxhumano izovalwa ngokuphela kwesikhathi. Ukusebenzisa okuthembekile kwe-UDP kusebenzisa izibali sikhathi ezimbili ukusetha ukuphela kwesikhathi. Esokuqala, isibali sikhathi esisebenzayo, sisetshenziselwa ukulinda impendulo evela kumsingathi wesilawuli kude. Uma ivutha ohlangothini lomthumeli, iphakethe lokugcina elithunyelwe lizothunjwa. Uma isibali sikhathi siphelelwa yisikhathi kumamukeli, kuzobe sekwenziwa isheke lamaphakethe alahlekile futhi izicelo zokulethwa kabusha ziyathunyelwa.

I-timer yesibili iyadingeka ukuvala uxhumano uma kungabikho ukuxhumana phakathi kwama-node. Ohlangothini lomthumeli, iqala ngokushesha ngemva kokuphelelwa yisikhathi kwesibali sikhathi, bese ilinda impendulo evela endaweni eyirimothi. Uma kungekho mpendulo phakathi nesikhathi esishiwo, uxhumano luyanqanyulwa futhi izinsiza ziyakhululwa. Ohlangothini olwamukelayo, isibali-sikhathi sokuxhuma siqalwa ngemva kokuphelelwa yisikhathi kabili komsebenzi. Lokhu kuyadingeka ukuze kuqinisekiswe ukulahlekelwa kwephakethe lokuqinisekisa. Lapho isibali sikhathi siphelelwa yisikhathi, uxhumano nalo luyanqanyulwa futhi izinsiza ziyakhululwa.

Umdwebo wesimo sokudlulisa we-UDP esithembekile

Izimiso zephrothokholi zisetshenziswa emshinini wesimo esilinganiselwe, izwe ngalinye elinesibopho somqondo othile wokucubungula iphakethe.
Umdwebo Wesifunda Othembekile we-UDP:

Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Kuvalwe - akusona isimo ngempela, isiqalo nesiphetho se-automaton. Okwezwe Kuvalwe kutholwa ibhulokhi yokulawula ukudlulisa, okuthi, isebenzisa iseva ye-UDP engavumelanisi, idlulisele amaphakethe ekuxhumekeni okufanele futhi iqale ukucubungula isimo.

FirstPacketSending - isimo sokuqala lapho ukuxhumana okuphumayo kulapho umlayezo uthunyelwa khona.

Kulesi simo, iphakethe lokuqala lemilayezo evamile liyathunyelwa. Emilayezweni ngaphandle kokuqinisekisa kokuthumela, lesi yisona kuphela isimo lapho umlayezo wonke uthunyelwa khona.

I-SendingCycle - isimo esiphansi sokudlulisa amaphakethe emiyalezo.

Ukushintshela kuyo kusuka kuhulumeni FirstPacketSending kwenziwe ngemuva kokuthi iphakethe lokuqala lomlayezo selithunyelwe. Kukulesi simo lapho konke ukuvunywa kanye nezicelo zokudluliswa kabusha kufika. Ukuphuma kuyo kungenzeka ezimweni ezimbili - uma uthunyelwa ngempumelelo umlayezo noma ngokuvala isikhathi.

I-FirstPacketReceived – isimo sokuqala somamukeli womlayezo.

Ihlola ukulunga kokuqala kokudluliswa, idale izakhiwo ezidingekayo, futhi ithumele ukuvuma kokuthola iphakethe lokuqala.

Kumlayezo oqukethe iphakethe elilodwa futhi othunyelwe ngaphandle kokusebenzisa ubufakazi bokulethwa, lesi ukuphela kwesifunda. Ngemva kokucubungula umlayezo onjalo, uxhumano luyavalwa.

Ukuhlangana – isimo esiyisisekelo sokwamukela amaphakethe emiyalezo.

Ibhala amaphakethe kusitoreji sesikhashana, ibheka ukulahleka kwephakethe, ithumela ukubonga ngokudiliva ibhulokhi yamaphakethe nawo wonke umyalezo, futhi ithumela izicelo zokulethwa kabusha kwamaphakethe alahlekile. Esimeni sokutholwa ngempumelelo kwawo wonke umlayezo, ukuxhumana kuya esimeni Kuqedile, ngaphandle kwalokho ukuphuma kwesikhathi kuyenzeka.

Kuqedile - ukuvala uxhumano uma uthola umlayezo wonke ngempumelelo.

Lesi simo siyadingeka ekuhlanganisweni komlayezo kanye necala lapho ukuqinisekiswa kokulethwa komlayezo kulahleka endleleni eya kumthumeli. Lesi simo siphuma ngesikhathi sokuvala, kodwa ukuxhumeka kubhekwa njengokuvalwe ngempumelelo.

Kujule kukhodi. iyunithi yokulawula ukudluliswa

Enye yezinto ezibalulekile ze-UDP Ethembekile ibhulokhi yokulawula ukudlulisela. Umsebenzi waleli bhulokhi ukugcina ukuxhumana kwamanje kanye nezakhi ezisizayo, ukusabalalisa amaphakethe angenayo ekuxhumaneni okuhambisanayo, ukunikeza isikhombimsebenzisi sokuthumela amaphakethe kuxhumo, nokusebenzisa i-protocol API. Ibhulokhi yokulawula ukudluliswa ithola amaphakethe kusuka kusendlalelo se-UDP futhi iwadlulisela emshinini wombuso ukuze acutshungulwe. Ukuze uthole amaphakethe, isebenzisa iseva ye-UDP engavumelanisi.
Amanye amalungu ekilasi le-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;    	
  //...
}

Ukuqaliswa kweseva ye-UDP engavumelanisiwe:

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);
}

Ekudluliselweni komlayezo ngakunye, kwakhiwa isakhiwo esiqukethe ulwazi mayelana nokuxhumana. Isakhiwo esinjalo sibizwa irekhodi lokuxhuma.
Amanye amalungu ekilasi le-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;
  //...
}

Kujule kukhodi. uthi

Amazwe asebenzisa umshini wezwe we-Reliable UDP protocol, lapho ukucubungula okuyinhloko kwamaphakethe kwenzeka. I-abstract class ReliableUdpState inikeza ukusebenzelana kwesifunda:

Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Yonke i-logic yephrothokholi isetshenziswa amakilasi ethulwe ngenhla, kanye neklasi elisizayo elihlinzeka ngezindlela ezimile, ezifana, isibonelo, ukwakha unhlokweni we-ReliableUdp kusuka kurekhodi lokuxhuma.

Okulandelayo, sizocubungula ngokuningiliziwe ukuqaliswa kwezindlela zokuxhumanisa ezinquma ama-algorithms ayisisekelo wephrothokholi.

Indlela ye-DisposeByTimeout

Indlela ye-DisposeByTimeout inesibopho sokukhulula izinsiza zokuxhuma ngemva kwesikhathi sokuvala kanye nokusayina ukuthunyelwa komlayezo okuyimpumelelo/okungaphumeleli.
I-ReliableUdpState.DisposeByTimeout:

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

Ibhalwe ngaphezulu kuphela esifundazweni Kuqedile.
Kuqediwe.DisposeByTimeout:

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

Indlela ye-ProcessPackets

Indlela ye-ProcessPackets inesibopho sokucutshungulwa okwengeziwe kwephakheji noma amaphakheji. Ibizwa ngokuqondile noma ngesibali sikhathi sokulinda sephakethe.

Iyakwazi Ukuhlangana indlela ibhalwe ngaphezulu futhi inesibopho sokuhlola amaphakethe alahlekile kanye nokushintshela kuhulumeni Kuqedile, uma uthola iphakethe lokugcina futhi uphase isheke eliyimpumelelo
Ukuhlanganisa.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);
  }
}

Iyakwazi I-SendingCycle le ndlela ibizwa kuphela ngesibali-sikhathi, futhi inesibopho sokuthumela kabusha umlayezo wokugcina, kanye nokunika amandla isibali sikhathi sokuxhuma.
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);
}

Iyakwazi Kuqedile indlela imisa isibali sikhathi futhi ithumela umlayezo kwababhalisile.
Kuqediwe.Amaphakethe wenqubo:

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

Indlela ye-ReceivePacket

Iyakwazi I-FirstPacketReceived umsebenzi oyinhloko wendlela ukunquma ukuthi ingabe iphakethe lomlayezo wokuqala lifikile ngempela kusixhumi esibonakalayo, kanye nokuqoqa umlayezo ohlanganisa iphakethe elilodwa.
I-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);
  }
}

Iyakwazi I-SendingCycle le ndlela ikhishiwe ukwamukela iziqinisekiso zokulethwa kanye nezicelo zokudlulisela kabusha.
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));
}

Iyakwazi Ukuhlangana ngendlela ye-ReceivePacket, umsebenzi oyinhloko wokuhlanganisa umlayezo ovela kumaphakethe angenayo uyenzeka.
Ukuhlanganisa.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);
  }
}

Iyakwazi Kuqedile okuwukuphela komsebenzi wendlela ukuthumela ukuvuma kabusha kokulethwa ngempumelelo komlayezo.
Kuqediwe.ReceivePacket:

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

Thumela Indlela Yephakethe

Iyakwazi FirstPacketSending le ndlela ithumela iphakethe lokuqala ledatha, noma, uma umlayezo ungadingi ukuqinisekiswa kokulethwa, umlayezo wonke.
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);
}

Iyakwazi I-SendingCycle ngale ndlela, ibhulokhi yamaphakethe ithunyelwa.
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 );
  }
}

Kujule kukhodi. Ukudala nokusungula Izixhumanisi

Manje njengoba sesibonile izifunda eziyisisekelo nezindlela ezisetshenziswa ukuphatha izifundazwe, ake sihlukanise izibonelo ezimbalwa zokuthi umthetho olandelwayo usebenza kanjani ngemininingwane eyengeziwe.
Umdwebo wokudlulisa idatha ngaphansi kwezimo ezijwayelekile:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Ake sihlolisise indalo irekhodi lokuxhuma ukuxhuma nokuthumela iphakethe lokuqala. Ukudlulisa njalo kuqaliswa uhlelo lokusebenza olubiza indlela ye-API yokuthumela umlayezo. Okulandelayo, indlela ye-StartTransmission ye-block control block isetshenziswa, ukuqala ukudluliswa kwedatha yomlayezo omusha.
Ukudala uxhumano oluphumayo:

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]);
}

Ithumela iphakethe lokuqala (FirstPacketSending state):

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);
}

Ngemva kokuthumela iphakethe lokuqala, umthumeli ungena kuhulumeni I-SendingCycle - linda ukuqinisekiswa kokulethwa kwephakheji.
Uhlangothi olwamukelayo, lisebenzisa indlela ye-EndReceive, lwamukela iphakethe elithunyelwe bese lidala elisha irekhodi lokuxhuma futhi idlulise leli phakethe, elinesihloko esihlukaniselwe ngaphambili, endleleni ye-ReceivePacket yesifunda ukuze icutshungulwe. I-FirstPacketReceived
Ukudala uxhumano ohlangothini lokwamukela:

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);
}

Ukwamukela iphakethe lokuqala kanye nokuthumela ukuvuma (isimo sokuqala sePacketReceived):

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);
  }
}

Kujule kukhodi. Ukuvala ukuxhumeka ngesikhathi sokuvala

Ukuphatha isikhathi sokuvala kuyingxenye ebalulekile ye-UDP Ethembekile. Cabanga ngesibonelo lapho i-node emaphakathi yehlulekile futhi ukulethwa kwedatha kuzo zombili izinkomba kube yinto engenakwenzeka.
Umdwebo wokuvala uxhumano ngesikhathi sokuvala:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Njengoba kubonakala kumdwebo, isibali sikhathi somsebenzi womthumeli siqala ngokushesha ngemva kokuthumela ibhulokhi yamaphakethe. Lokhu kwenzeka ngendlela ye-SendPacket yesifunda I-SendingCycle.
Ukunika amandla isibali sikhathi (isimo se-SendingCycle):

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

Izikhathi zesikhathi zisethwa lapho uxhumano kwakhiwa. Ngokuzenzakalelayo, i-ShortTimerPeriod iyimizuzwana emi-5. Esibonelweni isethwe kumasekhondi angu-1,5.

Ngoxhumo olungenayo, isibali sikhathi siqala ngemva kokuthola iphakethe ledatha elitholiwe lokugcina; lokhu kwenzeka ngendlela ye-ReceivePacket yesifunda. Ukuhlangana
Ukunika amandla isibali sikhathi (Isimo sokuhlanganisa):

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);
  // ...
}

Awasekho amaphakethe afikile kuxhumo olungenayo ngesikhathi elinde isibali-sikhathi esisebenzayo. Isibali sikhathi sihambile futhi sabiza indlela ye-ProcessPackets, lapho amaphakethe alahlekile atholakala futhi izicelo zokudiliva kabusha zithunyelwe okokuqala.
Ukuthumela izicelo zokuletha kabusha (Isimo sokuhlanganisa):

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);
  }
}

I-TimerSecondTry variable isethwe ukuze weqiniso. Lokhu okuguquguqukayo kunesibopho sokuqala kabusha isibali-sikhathi esisebenzayo.

Ngasohlangothini lomthumeli, isibali-sikhathi esisebenzayo siphinde sicushwe futhi iphakethe lokugcina elithunyelwe lithunyiwe.
Inika amandla ukuxhumeka kwesibali sikhathi sokuvala (isimo se-SendingCycle):

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

Ngemuva kwalokho, isibali sesikhathi sokuxhuma siqala ekuxhumekeni okuphumayo.
I-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);
}

Isikhathi sokuvala isibali sikhathi soxhumano ngamasekhondi angama-30 ngokuzenzakalelayo.

Ngemuva kwesikhashana, isibali-sikhathi esisebenzayo ohlangothini lomamukeli siyavutha futhi, izicelo zithunyelwa futhi, ngemuva kwalokho isibali-sikhathi sokuxhuma siqala ukuxhumana okungenayo.

Lapho izibali-sikhathi ezivaliwe zivutha, zonke izinsiza zawo womabili amarekhodi okuxhumana ziyakhululwa. Umthumeli ubika ukwehluleka kokulethwa kuhlelo lokusebenza olukhuphukayo (bona i-API Reliable UDP).
Ikhipha izinsiza zerekhodi loxhumano:

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);
  }
}

Kujule kukhodi. Ibuyisela ukudluliswa kwedatha

Umdwebo wokubuyisela ukudluliswa kwedatha uma iphakethe lilahlekile:Ukusetshenziswa kwephrothokholi ethembekile ye-Udp ye-.Net

Njengoba sekuxoxiwe kakade ekuvaleni uxhumano ngesikhathi sokuvala, lapho isibali sikhathi siphelelwa yisikhathi, umamukeli uzobheka amaphakethe alahlekile. Esimeni sokulahleka kwephakethe, uhlu lwenombolo yamaphakethe angafikanga kumamukeli luzohlanganiswa. Lezi zinombolo zifakwa ohlwini lwe-LostPackets lokuxhumana okuthile, futhi izicelo zokudiliva kabusha ziyathunyelwa.
Ukuthumela izicelo zokuletha kabusha amaphakheji (Isimo sokuhlanganisa):

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);
      }
    }
    // ...
  }
}

Umthumeli uzosamukela isicelo sokudiliva kabusha futhi athumele amaphakethe angekho. Kuyafaneleka ukuqaphela ukuthi ngalesi sikhathi umthumeli usevele eqalile uxhumano lwesikhathi sokuvala futhi, lapho isicelo samukelwe, sisethwa kabusha.
Ithumela kabusha amaphakethe alahlekile (isimo se-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));
}

Iphakethe elithunyelwe kabusha (iphakethe#3 kumdwebo) litholwa uxhumano olungenayo. Kuyahlolwa ukuze kuqinisekiswe ukuthi iwindi lokwamukela ligcwele futhi ukudluliswa kwedatha evamile kuyabuyiselwa.
Ihlola amahithi efasiteleni elitholayo (Isimo sokuhlanganisa):

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);
  }
  // ...
}

I-UDP API ethembekile

Ukuze uhlanganyele nephrothokholi yokudlulisa idatha, kunekilasi le-Udp elivulekile elithembekile, eligoqa phezu kwebhulokhi yokulawula ukudlulisa. Nawa amalungu abaluleke kakhulu ekilasi:

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()    
}

Imilayezo itholwa ngokubhalisa. Thumela isiginesha yendlela yokuphinda ushayele:

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

Umlayezo:

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

Ukuze ubhalisele uhlobo oluthile lomlayezo kanye/noma umthumeli othile, kusetshenziswa amapharamitha amabili ongawakhetha: I-ReliableUdpMessageTypes messageType kanye ne-IPEndPoint ipEndPoint.

Izinhlobo zomlayezo:

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

Umlayezo uthunyelwa ngendlela efanayo; ngale njongo, umthetho olandelwayo usebenzisa imodeli yokuhlela engavumelanisi:

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

Umphumela wokuthumela umlayezo uzoba yiqiniso - uma umlayezo ufinyelele kumamukeli ngempumelelo futhi ungamanga - uma uxhumano beluvalwe ngesikhathi sokuvala:

public bool EndSendMessage(IAsyncResult asyncResult)

isiphetho

Okuningi akukachazwa kulesi sihloko. Izindlela zokufanisa uchungechunge, okuhlukile nokuphatha amaphutha, ukuqaliswa kwezindlela zokuthumela imilayezo ezingavumelaniyo. Kodwa umnyombo wephrothokholi, incazelo ye-logic yokucubungula amaphakethe, ukusungula uxhumano, nokuphatha isikhathi sokuvala, kufanele kucace kuwe.

Inguqulo ebonisiwe yephrothokholi yokulethwa okuthembekile iqinile futhi iguquguquka ngokwanele ukuze ihlangabezane nezidingo ezichazwe ngaphambilini. Kodwa ngifuna ukwengeza ukuthi ukuqaliswa okuchazwe kungathuthukiswa. Isibonelo, ukukhulisa ukuphuma nokuguqula izikhathi zesikhathi, izindlela ezifana newindi lokushelelayo kanye ne-RTT zingangezwa kuphrothokholi, kuzophinde kube usizo ukusebenzisa indlela yokunquma i-MTU phakathi kwamanodi okuxhumana (kodwa kuphela uma imilayezo emikhulu ithunyelwa) .

Ngiyabonga ukunaka kwenu, ngibheke ngabomvu ukuphawula nokuphawula kwenu.

PS Kulabo abathanda imininingwane noma abafuna nje ukuhlola umthetho olandelwayo, nasi isixhumanisi sephrojekthi eku-GitHube:
Iphrojekthi ye-UDP ethembekile

Izixhumanisi eziwusizo nezindatshana

  1. Ukucaciswa kwephrothokholi ye-TCP: ngesiNgisi и ngesiRashiya
  2. Ukucaciswa kwephrothokholi ye-UDP: ngesiNgisi и ngesiRashiya
  3. Ingxoxo yephrothokholi ye-RUDP: okusalungiswa-ietf-sigtran-reliable-udp-00
  4. Iphrothokholi Yedatha Ethembekile: rfc 908 и rfc 1151
  5. Ukuqaliswa okulula kokuqinisekiswa kokulethwa nge-UDP: Thatha Ukulawula Okuphelele Kwenethiwekhi Yakho Nge-.NET kanye ne-UDP
  6. I-athikili echaza izindlela zokunqamula ze-NAT: Ukuxhumana Kontanga Kuyo Yonke Inethiwekhi Abahumushi Bamakheli
  7. Ukuqaliswa kwemodeli yokuhlela ye-asynchronous: Ukusebenzisa i-CLR Asynchronous Programming Model и Uyisebenzisa kanjani iphethini yokuklama ye-IAsyncResult
  8. Ihambisa imodeli yokuhlela engavumelanisi kuphethini esekelwe kumsebenzi (APM ku-TAP):
    I-TPL kanye ne-Traditional .NET Asynchronous Programming
    Isebenzisana Namanye Amaphethini Asynchronous Nezinhlobo

Update: Ngiyabonga imayorovp и sidristij ngombono wokwengeza umsebenzi kusixhumi esibonakalayo. Ukuhambisana komtapo wolwazi nezinhlelo zokusebenza ezindala akuphulwa, ngoba Uhlaka lwesine lusekela kokubili i-XP kanye neseva ka-4.

Source: www.habr.com

Engeza amazwana