Aplikasyon pwotokòl Reliable Udp pou .Net

Entènèt la chanje depi lontan. Youn nan pwotokòl entènèt prensipal yo, UDP yo itilize pa aplikasyon pou pa sèlman delivre datagram ak emisyon, men tou pou bay koneksyon kanmarad-a-kanmarad ant nœuds rezo yo. Akòz konsepsyon senp li yo, pwotokòl sa a gen anpil itilizasyon ki pa te planifye anvan; sepandan, enpèfeksyon pwotokòl la, tankou mank garanti livrezon, pa te disparèt. Atik sa a dekri yon aplikasyon pwotokòl livrezon garanti sou UDP.
Table of ContentsAntre
Pwotokòl kondisyon
Tèt UDP serye
Prensip jeneral pwotokòl la
Pwotokòl timeouts ak timeouts
Dyagram Eta Transmisyon UDP serye
Pi fon nan kòd la. Inite kontwòl transmisyon
Pi fon nan kòd la. Etazini

Pi fon nan kòd la. Kreye ak etabli koneksyon
Pi fon nan kòd la. Fèmen yon koneksyon akòz timeout
Pi fon nan kòd la. Rekiperasyon transfè done
API UDP serye
Konklizyon
Lyen itil ak atik

Antre

Achitekti orijinal entènèt la te anvizaje yon espas adrès inifòm kote chak ne te gen yon adrès IP global ak inik epi li te kapab kominike dirèkteman ak lòt nœuds. Koulye a, entènèt la, an reyalite, gen yon achitekti diferan - yon zòn nan adrès IP mondyal ak anpil zòn ki gen adrès prive kache dèyè aparèy NAT.Nan achitekti sa a, sèlman aparèy ki nan espas adrès mondyal la ka fasil pou kominike ak nenpòt moun ki sou rezo a paske yo gen yon adrès IP inik, globalman routabl. Yon ne ki sitiye sou yon rezo prive ka konekte ak lòt nœuds sou menm rezo a, epi tou konekte ak lòt nœuds byen li te ye nan espas adrès mondyal la. Se entèraksyon sa a reyalize lajman akòz mekanis tradiksyon adrès rezo a. Aparèy NAT, tankou routeurs Wi-Fi, kreye antre tab tradiksyon espesyal pou koneksyon sortan epi modifye adrès IP ak nimewo pò yo nan pake. Sa a pèmèt koneksyon sortan yo dwe fè soti nan yon rezo prive a gen tout pouvwa a nan espas adrès mondyal la. Men, an menm tan, aparèy NAT anjeneral bloke tout trafik fèk ap rantre sof si règ separe yo tabli pou koneksyon fèk ap rantre.

Achitekti entènèt sa a ase kòrèk pou entèraksyon kliyan-sèvè, lè kliyan yo ka lokalize sou rezo prive, ak sèvè yo gen yon adrès mondyal. Men, li kreye difikilte pou konekte dirèkteman de nœuds ant divès kalite rezo prive. Koneksyon dirèk de nœuds enpòtan pou aplikasyon kanmarad-a-kanmarad tankou transmisyon vwa (Skype), aksè òdinatè aleka (TeamViewer), oswa jwèt sou entènèt.

Youn nan metòd ki pi efikas pou etabli koneksyon kanmarad-a-kanmarad ant aparèy ki sitiye sou diferan rezo prive yo rele "trou pwensonaj". Teknik sa a pi souvan itilize ak aplikasyon ki baze sou pwotokòl UDP.

Men, si aplikasyon w lan mande pou garanti livrezon done, pou egzanp, ou transfere dosye ant òdinatè, Lè sa a, lè l sèvi avèk UDP pral prezante anpil difikilte akòz lefèt ke UDP se pa yon pwotokòl livrezon garanti epi li pa asire livrezon an nan pake nan lòd, kontrèman ak. pwotokòl TCP a.

Nan ka sa a, pou asire livrezon pakè garanti, li nesesè pou aplike yon pwotokòl nivo aplikasyon ki bay fonksyonalite ki nesesè yo ak travay sou UDP.

Mwen ta renmen sonje touswit ke gen yon teknik twou TCP pou etabli koneksyon TCP ant nœuds sou diferan rezo prive, men akòz mank de sipò pou li pa anpil aparèy NAT, anjeneral li pa konsidere kòm metòd prensipal la. konekte nœuds sa yo.

Pita nan atik sa a mwen pral sèlman konsidere aplikasyon an nan pwotokòl la livrezon garanti. Yo pral dekri aplikasyon teknik twou UDP pwensonaj nan atik sa yo.

Pwotokòl kondisyon

  1. Livrezon pake serye aplike atravè yon mekanis fidbak pozitif (sa yo rele rekonesans pozitif)
  2. Bezwen pou transfè efikas nan gwo done, i.e. pwotokòl la dwe evite retransmisyon pake ki pa nesesè
  3. Li ta dwe posib pou depase mekanis konfimasyon livrezon an (kapasite pou fonksyone kòm yon pwotokòl UDP "pi").
  4. Posiblite pou aplike mòd lòd, ak konfimasyon chak mesaj
  5. Inite debaz transmisyon done anba pwotokòl la dwe yon mesaj

Kondisyon sa yo lajman kowenside ak kondisyon pou Pwotokòl Done Fyab ki dekri nan rfc908 и rfc1151, e mwen te konte sou estanda sa yo pou devlope pwotokòl sa a.

Pou w konprann kondisyon sa yo, ann gade dyagram transfè done ant de nœuds rezo lè l sèvi avèk pwotokòl TCP ak UDP. Se pou nou pèdi yon pake nan tou de ka yo.
Transmèt done ki pa entèaktif sou TCP:Aplikasyon pwotokòl Reliable Udp pou .Net

Kòm ou ka wè nan dyagram nan, nan ka pèt pake a, TCP pral detekte pake a pèdi epi rapòte li bay moun k la lè li mande nimewo segman pèdi a.
Transfè done atravè pwotokòl UDP:Aplikasyon pwotokòl Reliable Udp pou .Net

UDP pa pran okenn mezi pou detekte pèt yo. Kontwòl erè transmisyon nan pwotokòl UDP a se antyèman responsablite aplikasyon an.

Deteksyon erè nan pwotokòl TCP a reyalize lè w etabli yon koneksyon ak yon ne final, estoke eta koneksyon sa a, endike kantite octets voye nan chak header pake, epi notifye resi yo lè l sèvi avèk yon nimewo rekonesans.

Anplis de sa, pou amelyore pèfòmans (sa vle di, voye plis pase yon segman san yo pa resevwa yon rekonesans), pwotokòl TCP a sèvi ak yon fenèt transmisyon sa yo rele - kantite byte done ke moun k ap voye segman an espere resevwa.

Ou ka jwenn plis detay sou pwotokòl TCP a nan rfc793, ak UDP nan rfc768, kote yo, an reyalite, defini.

Soti nan pi wo a, li klè ke yo nan lòd yo kreye yon pwotokòl livrezon mesaj serye sou UDP (apwe sa nou pral rele UDP serye), li oblije aplike mekanis transfè done ki sanble ak TCP. Savwa:

  • sove eta koneksyon
  • itilize nimero segman yo
  • sèvi ak pakè konfimasyon espesyal
  • sèvi ak yon mekanis fenèt senplifye pou ogmante debi pwotokòl

Anplis de sa, obligatwa:

  • siyal kòmansman yon mesaj pou asiyen resous pou koneksyon an
  • siyal fen yon mesaj, pou pase mesaj resevwa a pou yon aplikasyon en ak lib moute resous pwotokòl
  • pèmèt yon pwotokòl koneksyon espesifik pou enfim mekanis rekonesans livrezon an fonksyone kòm "pi" UDP.

Tèt UDP serye

Sonje byen, se yon datagram UDP encapsulé nan yon datagram IP. Pake Reliable UDP la kòmsadwa "vlope" nan yon datagram UDP.
Enkapsulasyon header UDP serye:Aplikasyon pwotokòl Reliable Udp pou .Net

Estrikti header Reliable UDP a byen senp:

Aplikasyon pwotokòl Reliable Udp pou .Net

  • Drapo - drapo kontwòl pake
  • MessageType – kalite mesaj, itilize pa aplikasyon en pou abònman nan mesaj espesifik
  • TransmissionId - nimewo transmisyon, ansanm ak adrès la ak pò moun k ap resevwa a, inikman idantifye koneksyon an
  • PacketNumber - nimewo pake
  • Opsyon - opsyon pwotokòl adisyonèl. Nan ka premye pake a, li itilize pou endike gwosè mesaj la

Drapo yo se jan sa a:

  • FirstPacket - premye pake mesaj la
  • NoAsk - mesaj la pa mande pou mekanis konfimasyon an aktive
  • LastPacket - dènye pake mesaj
  • RequestForPacket - pake konfimasyon oswa demann pou yon pake ki pèdi

Prensip jeneral pwotokòl la

Depi Reliable UDP konsantre sou garanti transmisyon yon mesaj ant de nœuds, li dwe kapab etabli yon koneksyon ak lòt bò a. Pou etabli yon koneksyon, bò k ap voye a voye yon pake ak drapo FirstPacket la, repons lan pral vle di koneksyon an te etabli. Tout pake repons, oswa, nan lòt mo, pake rekonesans, toujou mete valè a nan jaden PacketNumber nan yon sèl ki pi gran pase pi gwo valè PacketNumber nan pake ki rive avèk siksè. Chèn Opsyon pou premye pake ki voye a anrejistre gwosè mesaj la.

Yo itilize yon mekanis menm jan an pou konplete koneksyon an. Drapo LastPacket la mete nan dènye pake mesaj la. Pake repons lan endike nimewo a nan dènye pake a + 1, ki pou bò k ap resevwa a vle di livrezon siksè nan mesaj la.
Dyagram etablisman koneksyon ak revokasyon:Aplikasyon pwotokòl Reliable Udp pou .Net

Lè koneksyon an etabli, transfè done kòmanse. Done yo transmèt nan blòk nan pake. Chak blòk, eksepte dènye a, gen yon kantite pake fiks. Li egal a gwosè fenèt resevwa/transmèt la. Dènye blòk done a ka gen mwens pake. Apre voye chak blòk, bò k ap voye a ap tann pou konfimasyon livrezon, oswa yon demann pou relivrezon pake ki pèdi yo, kite fenèt resevwa/transmèt la louvri pou resevwa repons yo. Apre ou fin resevwa konfimasyon livrezon blòk, fenèt resepsyon/transmisyon an chanje epi yo voye pwochen blòk done yo.

Bò k ap resevwa a resevwa pakè yo. Yo tcheke chak pake pou wè si li tonbe nan fenèt transmisyon an. Pake ak doublons ki pa tonbe nan fenèt la elimine. Paske Depi gwosè fenèt la fiks ak menm bagay la tou pou moun k ap resevwa a ak moun k ap voye a, Lè sa a, nan ka livrezon yon blòk pake san pèt, fenèt la deplase pou resevwa pake nan pwochen blòk done yo epi yo voye yon konfimasyon livrezon. . Si fenèt la pa ranpli nan peryòd ki fikse pa revèy k ap travay la, yo pral fè yon chèk pou wè ki pakè yo pa te delivre epi yo pral voye demann pou relivyasyon.
Dyagram retransmisyon:Aplikasyon pwotokòl Reliable Udp pou .Net

Pwotokòl timeouts ak timeouts

Gen plizyè rezon ki fè yon koneksyon pa ka etabli. Pou egzanp, si pati k ap resevwa a se offline. Nan ka sa a, lè w ap eseye etabli yon koneksyon, koneksyon an pral fèmen akòz yon delè. Aplikasyon Reliable UDP a itilize de revèy pou mete delè. Premye a, revèy k ap travay la, yo itilize pou tann yon repons nan men lame a aleka. Si li deklanche sou bò k ap voye a, Lè sa a, dènye pake ki te voye a ap revoke. Si revèy la deklanche nan benefisyè a, Lè sa a, yon chèk fèt pou pake pèdi epi yo voye demann pou re-livrezon.

Dezyèm revèy la nesesè pou fèmen koneksyon an si pa gen okenn kominikasyon ant nœuds yo. Pou bò voye a, li kòmanse imedyatman apre revèy k ap travay la ekspire, epi tann pou yon repons soti nan ne a aleka. Si pa gen okenn repons nan peryòd espesifye a, koneksyon an sispann epi resous yo lage. Pou bò benefisyè a, koneksyon fèmen revèy la kòmanse apre revèy travay la dife de fwa. Sa nesesè pou asire w kont pèt pake rekonesans la. Lè revèy la dife, koneksyon an tou sispann ak resous yo lage.

Dyagram Eta Transmisyon UDP serye

Prensip fonksyònman pwotokòl la aplike nan yon machin eta fini, chak eta ki responsab pou yon lojik pwosesis espesifik pake.
Dyagram eta UDP serye:

Aplikasyon pwotokòl Reliable Udp pou .Net

Fèmen - se pa aktyèlman yon eta, li se pwen an kòmanse ak fen pou machin nan. Pou leta Fèmen yo resevwa yon blòk kontwòl transmisyon, ki, mete ann aplikasyon yon sèvè UDP asynchrone, redireksyon pake yo nan koneksyon ki apwopriye yo epi kòmanse pwosesis eta a.

FirstPacketSending – Eta inisyal la nan ki koneksyon sortan an se lè voye yon mesaj.

Nan eta sa a, yo voye premye pake pou mesaj regilye yo. Pou mesaj san yo pa konfimasyon voye, sa a se eta a sèlman - se tout mesaj la voye nan li.

SendingCycle – eta debaz pou transmèt pakè mesaj.

Tranzisyon nan li soti nan eta a FirstPacketSending te pote apre voye premye pake mesaj la. Se nan eta sa a ke tout rekonesans ak demann pou retransmisyon vini. Sòti soti nan li se posib nan de ka - nan ka livrezon siksè nan mesaj la oswa akòz yon delè.

FirstPacketReceived - premye eta pou moun k ap resevwa mesaj la.

Li tcheke kòrèkteman nan kòmansman an nan transmisyon an, kreye estrikti ki nesesè yo, epi voye yon rekonesans pou resevwa premye pake a.

Pou yon mesaj ki gen yon sèl pake epi voye san yo pa itilize yon konfimasyon livrezon, sa a se eta a sèlman. Apre yo fin trete yon mesaj konsa, koneksyon an fèmen.

Montan – eta debaz pou resevwa pakè mesaj.

Li ekri pake nan depo tanporè, tcheke pou pèt pake, voye konfimasyon nan livrezon yon blòk nan pake ak mesaj la tout antye, epi voye demann pou relivrezon nan pake ki pèdi. Si tout mesaj la resevwa avèk siksè, koneksyon an antre nan Konplete, otreman yon sòti timeout rive.

Konplete – fèmen koneksyon an si tout mesaj la resevwa avèk siksè.

Eta sa a nesesè pou asanble mesaj ak pou ka a lè konfimasyon livrezon mesaj te pèdi sou wout la nan moun k la. Eta sa a soti nan yon delè, men yo konsidere koneksyon an byen fèmen.

Pi fon nan kòd la. Inite kontwòl transmisyon

Youn nan eleman kle Reliable UDP se blòk kontwòl transmisyon an. Objektif blòk sa a se pou estoke koneksyon aktyèl ak eleman oksilyè, distribye pake k ap rantre nan koneksyon ki apwopriye yo, bay yon koòdone pou voye pake nan koneksyon an, epi aplike API pwotokòl la. Blòk kontwòl transmisyon an resevwa pakè ki soti nan kouch UDP a epi redireksyon yo nan machin leta a pou trete. Pou resevwa pakè, li aplike yon sèvè UDP asynchrone.
Kèk manm nan klas 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;    	
  //...
}

Aplikasyon yon sèvè UDP asynchrone:

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

Pou chak transmisyon mesaj, yo kreye yon estrikti ki gen enfòmasyon sou koneksyon. Yo rele estrikti sa a dosye koneksyon.
Kèk manm nan klas 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;
  //...
}

Pi fon nan kòd la. Etazini

Etazini aplike machin eta a nan pwotokòl Reliable UDP, nan ki pwosesis prensipal la nan pake rive. Klas abstrè ReliableUdpState bay yon koòdone pou eta:

Aplikasyon pwotokòl Reliable Udp pou .Net

Tout lojik pwotokòl la aplike pa klas yo prezante pi wo a, ansanm ak yon klas oksilyè ki bay metòd estatik, tankou, pou egzanp, konstwi header ReliableUdp soti nan dosye koneksyon an.

Apre sa, nou pral konsidere an detay aplikasyon metòd koòdone ki defini algoritm debaz pwotokòl la.

DisposeByTimeout metòd

Metòd DisposeByTimeout responsab pou divilge resous koneksyon lè yon delè ekspire ak pou siyal siksè/echèk livrezon mesaj.
ReliableUdpState.DisposeByTimeout:

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

Li se sèlman anile nan eta a Konplete.
Completed.DisposeByTimeout:

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

Metòd ProcessPackets

Metòd ProcessPackets la responsab pou pwosesis adisyonèl nan pake a oswa pakè yo. Yo rele dirèkteman oswa atravè yon revèy datant pake.

Kapab Montan se metòd la anile epi li responsab pou tcheke pou pake ki pèdi ak tranzisyon nan eta Konplete, nan ka resevwa dènye pake a ak pase chèk la avèk siksè
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);
  }
}

Kapab SendingCycle metòd sa a rele sèlman sou yon revèy, epi li responsab pou renvoie dènye mesaj la, osi byen ke pou kòmanse revèy la fèmen koneksyon.
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);
}

Kapab Konplete Metòd la sispann revèy la kouri epi transmèt yon mesaj bay abonnés.
Completed.ProcessPackets:

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

Metòd ReceivePacket

Kapab FirstPacketReceived Travay prensipal la nan metòd la se detèmine si premye pake mesaj la aktyèlman te rive sou koòdone a, epi tou rasanble yon mesaj ki fòme ak yon pake sèl.
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);
  }
}

Kapab SendingCycle metòd sa a pase pou aksepte konfimasyon livrezon ak demann retransmisyon.
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));
}

Kapab Montan nan metòd la ReceivePacket, travay prensipal la nan rasanble mesaj la soti nan pake fèk ap rantre rive.
Asanble.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);
  }
}

Kapab Konplete Travay la sèlman nan metòd la se voye yon konfimasyon repete nan livrezon siksè nan mesaj la.
Konplete.ResevwaPake:

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

SendPacket metòd

Kapab FirstPacketSending Metòd sa a voye premye pake done, oswa, si mesaj la pa mande pou konfimasyon livrezon, tout mesaj la.
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);
}

Kapab SendingCycle nan metòd sa a, yo voye yon blòk pake.
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 );
  }
}

Pi fon nan kòd la. Kreye ak etabli koneksyon

Kounye a ke nou abitye ak eta debaz yo ak metòd yo itilize pou trete eta yo, nou ka gade nan yon ti kras plis detay nan plizyè egzanp operasyon pwotokòl la.
Dyagram transfè done nan kondisyon nòmal:Aplikasyon pwotokòl Reliable Udp pou .Net

Ann pran yon gade pi pre nan kreyasyon an dosye koneksyon konekte epi voye premye pake a. Transfè a toujou inisye pa aplikasyon an ki rele metòd API pou voye mesaj la. Apre sa, metòd StartTransmission nan blòk kontwòl transmisyon yo itilize, kòmanse transmisyon done pou nouvo mesaj la.
Kreye yon koneksyon sortan:

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

Voye premye pake a (eta 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);
}

Apre voye premye pake a, moun k ap voye a antre nan eta a SendingCycle - tann konfimasyon livrezon pake a.
Bò k ap resevwa a, lè l sèvi avèk metòd EndReceive, aksepte pake voye a epi kreye yon nouvo dosye koneksyon epi li pase pake sa a, ak yon header analize davans, nan metòd eta ReceivePacket pou trete FirstPacketReceived
Kreye yon koneksyon sou bò k ap resevwa a:

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

Resevwa premye pake a epi voye yon rekonesans (FirstPacketReceived eta):

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

Pi fon nan kòd la. Fèmen yon koneksyon akòz timeout

Manyen delè se yon pati enpòtan nan Reliable UDP. Konsidere yon egzanp kote yon ne entèmedyè echwe epi livrezon done nan toude direksyon yo vin enposib.
Dyagram pou fèmen yon koneksyon pa timeout:Aplikasyon pwotokòl Reliable Udp pou .Net

Kòm ou ka wè nan dyagram nan, revèy k ap travay moun k ap travay la kòmanse imedyatman apre voye yon blòk nan pake. Sa rive nan metòd SendPacket nan eta a SendingCycle.
Pèmèt revèy k ap travay la (eta SenddingCycle):

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

Peryòd revèy yo mete lè koneksyon an kreye. Pa default, ShortTimerPeriod se 5 segonn. Nan egzanp lan, li mete sou 1,5 segonn.

Pou yon koneksyon fèk ap rantre, revèy la kòmanse apre li fin resevwa dènye pake done ki te resevwa a; sa rive nan metòd ReceivePacket nan eta a. Montan
Pèmèt revèy k ap travay la (eta asanble):

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

Koneksyon an fèk ap rantre pa t resevwa plis pake pandan tan ap tann pou revèy k ap travay la. Revèy la te tire epi li te rele metòd ProcessPackets, ki te detekte pakè ki te pèdi yo epi li te voye demann relivrezon pou premye fwa.
Voye demann relivrezon (eta asanble):

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

Varyab TimerSecondTry mete sou vrè. Varyab sa a responsab pou rekòmanse revèy k ap travay la.

Sou bò moun k ap voye a, yon revèy k ap travay tou deklanche epi dènye pake ki te voye a ap remèt.
Pèmèt revèy fèmen koneksyon an (eta SenddingCycle):

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

Apre sa, revèy fèmen koneksyon an kòmanse nan koneksyon sortan an.
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);
}

Peryòd delè defo pou koneksyon fèmen revèy la se 30 segonn.

Apre yon ti tan, revèy k ap travay sou bò k ap resevwa a deklanche ankò, demann yo voye ankò, apre yo fin revèy la fèmen koneksyon pou koneksyon fèk ap rantre a te kòmanse.

Lè revèy yo fèmen, tout resous tou de dosye koneksyon yo lage. Moun ki voye a rapòte echèk livrezon an nan aplikasyon an en (gade API Reliable UDP).
Divilge resous dosye koneksyon:

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

Pi fon nan kòd la. Rekiperasyon transfè done

Dyagram rekiperasyon transmisyon done nan ka pèt pake:Aplikasyon pwotokòl Reliable Udp pou .Net

Kòm deja diskite nan fèmen yon koneksyon pa timeout, lè revèy k ap travay la ekspire, reseptè a pral tcheke pou pake pèdi. Si gen pèt pake, yo pral konpile yon lis kantite pake ki pa rive jwenn moun k ap resevwa a. Nimewo sa yo antre nan etalaj LostPackets nan yon koneksyon espesifik epi yo voye demann pou re-livrezon.
Voye demann pou re-livre pakè (eta asanble):

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

Moun k ap voye a pral aksepte demann lan ankò epi li pral voye pakè ki manke yo ankò. Li se vo anyen ke nan moman sa a moun k la te deja kòmanse yon revèy fèmen koneksyon epi, lè li resevwa yon demann, li reset.
Revoke pake ki pèdi (eta SenddingCycle):

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

Koneksyon k ap fèk ap rantre an resevwa pake resent la (pake #3 nan dyagram nan). Yo fè yon chèk pou asire ke fenèt resevwa a plen epi transmisyon done nòmal retabli.
Tcheke pou frape nan fenèt k ap resevwa a (eta asanble):

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 serye

Pou kominike avèk pwotokòl transfè done a, gen yon klas ouvè Reliable Udp, ki se yon anbalaj sou blòk kontwòl transmisyon an. Men manm klas ki pi enpòtan yo:

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

Resevwa mesaj fèt pa abònman. Siyati delege pou metòd rapèl:

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

Mesaj:

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

Pou abònman nan yon kalite mesaj espesifik ak/oswa yon moun k espesifik, de paramèt opsyonèl yo itilize: ReliableUdpMessageTypes messageType ak IPEndPoint ipEndPoint.

Kalite mesaj:

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

Se mesaj la voye asynchrone; pou objektif sa a, pwotokòl la aplike yon modèl pwogramasyon asynchrone:

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

Rezilta a nan voye yon mesaj pral vre - si mesaj la avèk siksè rive jwenn moun k ap resevwa a ak fo - si koneksyon an te fèmen akòz yon delè:

public bool EndSendMessage(IAsyncResult asyncResult)

Konklizyon

Anpil pa te dekri nan sijè ki abòde lan atik sa a. Mekanis kowòdinasyon fil, eksepsyon ak manyen erè, aplikasyon metòd asynchrone voye mesaj. Men, nwayo a nan pwotokòl la, yon deskripsyon nan lojik la pou trete pake, etabli yon koneksyon ak travay soti timeouts, ta dwe vin pi klè pou ou.

Vèsyon an demontre nan pwotokòl livrezon serye a se byen solid ak fleksib, epi li satisfè kondisyon yo defini deja. Men, mwen vle ajoute ke aplikasyon ki dekri a ka amelyore. Pou egzanp, ogmante debi ak chanje dinamik peryòd revèy, ou ka ajoute mekanis tankou fenèt glisman ak RTT nan pwotokòl la; li pral itil tou pou aplike yon mekanis pou detèmine MTU ki genyen ant nœuds koneksyon (men sèlman nan ka voye. gwo mesaj).

Mèsi pou atansyon ou, mwen tann kòmantè ou ak kòmantè ou yo.

PS Pou moun ki enterese nan detay oswa jis vle teste pwotokòl la, isit la se lyen ki mennen nan pwojè a sou GitHube:
Pwojè UDP serye

Lyen itil ak atik

  1. TCP Pwotokòl Espesifikasyon: an anglè и an Ris
  2. Espesifikasyon pwotokòl UDP: an anglè и an Ris
  3. Diskisyon sou pwotokòl RUDP: bouyon-ietf-sigtran-reliable-udp-00
  4. Pwotokòl Done serye: rfc908 и rfc1151
  5. Yon senp aplikasyon konfimasyon livrezon sou UDP: Pran kontwòl total sou rezo ou ak .NET ak UDP
  6. Yon atik ki dekri mekanis pou simonte NAT yo: Kominikasyon kanmarad a kamarad atravè tradiktè adrès rezo yo
  7. Aplikasyon modèl pwogram asynchrone a: Aplike CLR Asynchrone Programming Model и Ki jan yo aplike modèl konsepsyon IAsyncResult la
  8. Deplase modèl pwogram asynchrone a nan modèl asynchrone ki baze sou travay (APM pou TAP):
    TPL ak tradisyonèl .NET Asynchrone Programming
    Interop ak lòt modèl asynchrone ak kalite

Mizajou: Mèsi mayorovp и sidristij pou lide pou ajoute yon travay nan koòdone a. Konpatibilite bibliyotèk la ak pi gran sistèm operasyon yo pa afekte, paske 4yèm fondasyon an sipòte tou de sèvè XP ak 2003.

Sous: www.habr.com

Add nouvo kòmantè