Imuse ti Ilana Udp Gbẹkẹle fun .Net

Intanẹẹti ti yipada ni igba pipẹ sẹhin. Ọkan ninu awọn ilana akọkọ ti Intanẹẹti - UDP jẹ lilo nipasẹ awọn ohun elo kii ṣe lati fi awọn datagram ati awọn igbohunsafefe ranṣẹ nikan, ṣugbọn tun lati pese awọn asopọ “ẹlẹgbẹ-si-ẹlẹgbẹ” laarin awọn apa nẹtiwọki. Nitori apẹrẹ ti o rọrun, ilana yii ni ọpọlọpọ awọn lilo ti a ko gbero tẹlẹ, sibẹsibẹ, awọn ailagbara ti ilana naa, gẹgẹbi aini ifijiṣẹ iṣeduro, ko ti sọnu nibikibi. Nkan yii ṣe apejuwe imuse ti ilana ifijiṣẹ iṣeduro lori UDP.
Awọn akoonu:Ifihan
Awọn ibeere Ilana
Akọsori UDP ti o gbẹkẹle
Awọn ipilẹ gbogbogbo ti ilana naa
Awọn akoko akoko ati awọn aago ilana
Gbẹkẹle UDP gbigbe ipinle aworan atọka
Jinle sinu koodu. gbigbe Iṣakoso kuro
Jinle sinu koodu. awọn ipinlẹ

Jinle sinu koodu. Ṣiṣẹda ati Igbekale Awọn isopọ
Jinle sinu koodu. Pipade asopọ ni akoko ipari
Jinle sinu koodu. mimu-pada sipo data gbigbe
UDP API ti o gbẹkẹle
ipari
Wulo ìjápọ ati ìwé

Ifihan

Atilẹba faaji ti Intanẹẹti dawọle aaye adirẹsi isokan ninu eyiti ipade kọọkan ni adiresi IP agbaye ati alailẹgbẹ ati pe o le ṣe ibaraẹnisọrọ taara pẹlu awọn apa miiran. Bayi Intanẹẹti, ni otitọ, ni faaji ti o yatọ - agbegbe kan ti awọn adirẹsi IP agbaye ati ọpọlọpọ awọn agbegbe pẹlu awọn adirẹsi ikọkọ ti o farapamọ lẹhin awọn ẹrọ NAT.Ninu faaji yii, awọn ẹrọ nikan ni aaye adirẹsi agbaye le ni irọrun ṣe ibasọrọ pẹlu ẹnikẹni lori nẹtiwọọki nitori pe wọn ni alailẹgbẹ, adiresi IP ti o ṣee ṣe kaakiri agbaye. Ipade lori nẹtiwọọki aladani le sopọ si awọn apa miiran lori nẹtiwọọki kanna, ati pe o tun le sopọ si awọn apa miiran ti a mọ daradara ni aaye adirẹsi agbaye. Ibaraṣepọ yii waye ni pataki nitori ẹrọ itumọ adirẹsi nẹtiwọki. Awọn ẹrọ NAT, gẹgẹbi awọn olulana Wi-Fi, ṣẹda awọn titẹ sii tabili itumọ pataki fun awọn asopọ ti njade ati ṣatunṣe awọn adirẹsi IP ati awọn nọmba ibudo ni awọn apo-iwe. Eyi ngbanilaaye awọn asopọ ti njade lati nẹtiwọọki ikọkọ si awọn agbalejo ni aaye adirẹsi agbaye. Ṣugbọn ni akoko kanna, awọn ẹrọ NAT nigbagbogbo ṣe idiwọ gbogbo awọn ijabọ ti nwọle ayafi ti awọn ofin lọtọ fun awọn asopọ ti nwọle ti ṣeto.

Awọn faaji ti Intanẹẹti jẹ deede to fun ibaraẹnisọrọ alabara-olupin, nibiti awọn alabara le wa ni awọn nẹtiwọọki aladani, ati awọn olupin ni adirẹsi agbaye kan. Ṣugbọn o ṣẹda awọn iṣoro fun asopọ taara ti awọn apa meji laarin orisirisi ikọkọ nẹtiwọki. Isopọ taara laarin awọn apa meji jẹ pataki fun awọn ohun elo ẹlẹgbẹ-si-ẹlẹgbẹ gẹgẹbi gbigbe ohun (Skype), nini iraye si latọna jijin si kọnputa (TeamViewer), tabi ere ori ayelujara.

Ọkan ninu awọn ọna ti o munadoko julọ fun idasile asopọ ẹlẹgbẹ-si-ẹlẹgbẹ laarin awọn ẹrọ lori awọn nẹtiwọọki aladani oriṣiriṣi ni a pe ni iho iho. Ilana yii jẹ lilo julọ pẹlu awọn ohun elo ti o da lori ilana UDP.

Ṣugbọn ti ohun elo rẹ ba nilo ifijiṣẹ iṣeduro ti data, fun apẹẹrẹ, o gbe awọn faili laarin awọn kọnputa, lẹhinna lilo UDP yoo ni awọn iṣoro pupọ nitori otitọ pe UDP kii ṣe ilana ifijiṣẹ iṣeduro ati pe ko pese ifijiṣẹ soso ni ibere, ko dabi TCP. Ilana.

Ni ọran yii, lati rii daju ifijiṣẹ soso ti o ni iṣeduro, o nilo lati ṣe ilana ilana Layer ohun elo ti o pese iṣẹ ṣiṣe pataki ati ṣiṣẹ lori UDP.

Mo fẹ lati ṣe akiyesi lẹsẹkẹsẹ pe ilana fifun iho TCP kan wa fun idasile awọn asopọ TCP laarin awọn apa ni awọn nẹtiwọọki aladani oriṣiriṣi, ṣugbọn nitori aini atilẹyin fun nipasẹ ọpọlọpọ awọn ẹrọ NAT, kii ṣe igbagbogbo bi ọna akọkọ lati sopọ. iru apa.

Fun iyoku ti nkan yii, Emi yoo dojukọ nikan lori imuse ti ilana ifijiṣẹ iṣeduro. Imuse ti UDP iho ilana punching yoo wa ni apejuwe ninu awọn wọnyi ìwé.

Awọn ibeere Ilana

  1. Ifijiṣẹ soso ti o gbẹkẹle ti a ṣe nipasẹ ẹrọ esi rere (eyiti a pe ni ijẹwọ rere)
  2. Iwulo fun gbigbe daradara ti data nla, i.e. Ilana naa gbọdọ yago fun isọdọtun soso ti ko wulo
  3. O yẹ ki o ṣee ṣe lati fagilee ẹrọ ijẹrisi ifijiṣẹ (agbara lati ṣiṣẹ bi ilana UDP “funfun”)
  4. Agbara lati ṣe ipo aṣẹ, pẹlu ìmúdájú ti ifiranṣẹ kọọkan
  5. Ẹka ipilẹ ti gbigbe data lori ilana gbọdọ jẹ ifiranṣẹ kan

Awọn ibeere wọnyi ṣe deede pẹlu awọn ibeere Ilana Data Gbẹkẹle ti a ṣalaye ninu Rfc 908 и Rfc 1151, ati pe Mo gbarale awọn iṣedede wọnyẹn nigbati n ṣe agbekalẹ ilana yii.

Lati loye awọn ibeere wọnyi, jẹ ki a wo akoko gbigbe data laarin awọn apa nẹtiwọki meji nipa lilo awọn ilana TCP ati UDP. Jẹ ki ni igba mejeeji a yoo ni ọkan soso sọnu.
Gbigbe data ti kii ṣe ibaraẹnisọrọ lori TCP:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Gẹgẹbi o ti le rii lati aworan atọka, ni ọran ti pipadanu apo, TCP yoo rii apo-iwe ti o sọnu ki o jabo si olufiranṣẹ nipa bibeere fun nọmba ti apakan ti o sọnu.
Gbigbe data nipasẹ ilana UDP:Imuse ti Ilana Udp Gbẹkẹle fun .Net

UDP ko ṣe awọn igbesẹ wiwa pipadanu eyikeyi. Iṣakoso ti awọn aṣiṣe gbigbe ni ilana UDP jẹ ojuṣe ohun elo naa patapata.

Wiwa aṣiṣe ninu ilana TCP ti waye nipasẹ iṣeto asopọ pẹlu ipade ipari, titoju ipo asopọ yẹn, nfihan nọmba awọn baiti ti a fi ranṣẹ si akọsori apo-iwe kọọkan, ati ifitonileti awọn owo-owo nipa lilo nọmba ifọwọsi.

Ni afikun, lati mu ilọsiwaju ṣiṣẹ (ie fifiranṣẹ diẹ ẹ sii ju apakan kan laisi gbigba ifọwọsi), Ilana TCP nlo ohun ti a pe ni window gbigbe - nọmba awọn baiti ti data ti olufiranṣẹ ti apakan naa nireti lati gba.

Fun alaye diẹ sii nipa ilana TCP, wo Rfc 793, lati UDP si Rfc 768ibi ti, ni pato, ti won ti wa ni telẹ.

Lati eyi ti o wa loke, o han gbangba pe lati le ṣẹda ilana ifijiṣẹ ifiranṣẹ ti o gbẹkẹle lori UDP (lẹhinna ti a tọka si bi UDP ti o gbẹkẹle), o nilo lati ṣe awọn ọna gbigbe data ti o jọra si TCP. Eyun:

  • fi ipo asopọ
  • lo awọn nọmba apa
  • lo pataki ìmúdájú jo
  • lo ẹrọ ṣiṣe window ti o rọrun lati mu iwọn iṣelọpọ pọ si

Ni afikun, o nilo:

  • ifihan agbara ibẹrẹ ifiranṣẹ, lati pin awọn orisun fun asopọ
  • ifihan opin ifiranṣẹ kan, lati fi ifiranṣẹ ti o gba ranṣẹ si ohun elo oke ati tu awọn orisun ilana silẹ
  • gba ilana asopọ-pato lati mu ẹrọ ijẹrisi ifijiṣẹ ṣiṣẹ bi “mimọ” UDP

Akọsori UDP ti o gbẹkẹle

Ranti pe datagram UDP kan ti wa ni idalẹnu ninu datagram IP kan. Pakẹti UDP Gbẹkẹle jẹ “ti a we” ni deede sinu datagram UDP kan.
Ifipamo akọsori UDP ti o gbẹkẹle:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Eto ti akọsori UDP Gbẹkẹle jẹ ohun rọrun:

Imuse ti Ilana Udp Gbẹkẹle fun .Net

  • Awọn asia - package iṣakoso awọn asia
  • MessageType – iru ifiranṣẹ ti awọn ohun elo oke lo lati ṣe alabapin si awọn ifiranṣẹ kan pato
  • TransmissionId - nọmba gbigbe, pẹlu adirẹsi ati ibudo olugba, ṣe idanimọ asopọ ni iyasọtọ
  • PacketNumber - nọmba apo
  • Awọn aṣayan - afikun awọn aṣayan Ilana. Ninu ọran ti apo akọkọ, a lo lati tọka iwọn ifiranṣẹ naa

Awọn asia jẹ bi wọnyi:

  • FirstPacket - apo akọkọ ti ifiranṣẹ naa
  • NoAsk - ifiranṣẹ naa ko nilo ẹrọ ifọwọsi lati mu ṣiṣẹ
  • LastPacket - apo ikẹhin ti ifiranṣẹ naa
  • RequestForPacket - apo idaniloju tabi ìbéèrè fun apo-iwe ti o sọnu

Awọn ipilẹ gbogbogbo ti ilana naa

Niwọn igba ti UDP ti o gbẹkẹle ti wa ni idojukọ lori gbigbe ifiranṣẹ ti o ni idaniloju laarin awọn apa meji, o gbọdọ ni anfani lati fi idi asopọ kan mulẹ pẹlu apa keji. Lati fi idi asopọ kan mulẹ, olufiranṣẹ naa firanṣẹ apo-iwe kan pẹlu asia FirstPacket, idahun si eyiti yoo tumọ si asopọ ti wa ni idasilẹ. Gbogbo awọn apo-iwe idahun, tabi, ni awọn ọrọ miiran, awọn idii ijẹwọ, nigbagbogbo ṣeto iye ti aaye PacketNumber si ọkan diẹ sii ju iye PacketNumber ti o tobi julọ ti awọn apo-iwe ti o gba ni aṣeyọri. Aaye Awọn aṣayan fun apo akọkọ ti a firanṣẹ jẹ iwọn ifiranṣẹ naa.

Ilana ti o jọra ni a lo lati fopin si asopọ kan. Asia LastPacket ti ṣeto lori apo-iwe ti o kẹhin ti ifiranṣẹ naa. Ninu apo idahun, nọmba ti apo-iwe ti o kẹhin + 1 jẹ itọkasi, eyiti fun ẹgbẹ gbigba tumọ si ifijiṣẹ aṣeyọri ti ifiranṣẹ naa.
Idasile asopọ ati aworan ifopinsi:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Nigbati asopọ ba ti fi idi mulẹ, gbigbe data bẹrẹ. Data ti wa ni gbigbe ni awọn bulọọki ti awọn apo-iwe. Àkọsílẹ kọọkan, ayafi ti o kẹhin, ni nọmba ti o wa titi ti awọn apo-iwe. O jẹ dogba si iwọn window gbigba / atagba. Awọn ti o kẹhin Àkọsílẹ ti data le ni díẹ awọn apo-iwe. Lẹhin fifiranṣẹ bulọọki kọọkan, ẹgbẹ fifiranṣẹ n duro de ijẹrisi ifijiṣẹ tabi ibeere lati tun fi awọn apo-iwe ti o sọnu silẹ, nlọ window gbigba / atagba ṣii lati gba awọn idahun. Lẹhin gbigba ijẹrisi ti ifijiṣẹ bulọọki, window gbigba / atagba n yipada ati bulọki data atẹle ti firanṣẹ.

Awọn ẹgbẹ gbigba gba awọn apo-iwe. A ṣe ayẹwo apo-iwe kọọkan lati rii boya o ṣubu laarin window gbigbe. Awọn apo-iwe ati awọn ẹda-ẹda ti ko ṣubu sinu window ti wa ni filtered jade. Nitori Ti iwọn ti window ba wa ni ipilẹ ati kanna fun olugba ati olufiranṣẹ, lẹhinna ninu ọran ti bulọọki ti awọn apo-iwe ti a firanṣẹ laisi pipadanu, window naa ti yipada lati gba awọn apo-iwe ti bulọọki data atẹle ati ijẹrisi ifijiṣẹ jẹ rán. Ti window ko ba kun laarin akoko ti a ṣeto nipasẹ aago iṣẹ, lẹhinna ṣayẹwo kan yoo bẹrẹ lori eyiti awọn apo-iwe ko ti jiṣẹ ati awọn ibeere fun irapada yoo firanṣẹ.
Àwòrán ìtúngbéjáde:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Awọn akoko akoko ati awọn aago ilana

Awọn idi pupọ lo wa ti asopọ ko ṣe fi idi mulẹ. Fun apẹẹrẹ, ti ẹgbẹ gbigba ba wa ni aisinipo. Ni idi eyi, nigba igbiyanju lati fi idi asopọ kan mulẹ, asopọ naa yoo wa ni pipade nipasẹ akoko-akoko. Imuse UDP Gbẹkẹle nlo awọn aago meji lati ṣeto awọn akoko ipari. Ni akọkọ, aago iṣẹ, ni a lo lati duro fun esi lati ọdọ agbalejo latọna jijin. Ti o ba jẹ ina ni ẹgbẹ olufiranṣẹ, lẹhinna soso ti a firanṣẹ kẹhin jẹ resent. Ti aago ba dopin ni olugba, lẹhinna ṣayẹwo fun awọn apo-iwe ti o sọnu ni a ṣe ati awọn ibeere fun irapada ni a firanṣẹ.

Aago keji nilo lati pa asopọ pọ ni ọran ti aini ibaraẹnisọrọ laarin awọn apa. Fun ẹgbẹ olufiranṣẹ, o bẹrẹ lẹsẹkẹsẹ lẹhin aago iṣẹ dopin, o si duro de esi lati oju ipade latọna jijin. Ti ko ba si esi laarin awọn pàtó kan akoko, awọn asopọ ti wa ni fopin ati awọn orisun ti wa ni idasilẹ. Fun ẹgbẹ gbigba, aago asopọ isunmọ bẹrẹ lẹhin ti aago iṣẹ dopin lẹẹmeji. Eyi jẹ pataki lati ṣe iṣeduro lodi si ipadanu ti apo-ẹri ijẹrisi naa. Nigbati aago ba dopin, asopọ naa tun ti fopin ati awọn orisun ti tu silẹ.

Gbẹkẹle UDP gbigbe ipinle aworan atọka

Awọn ilana ti ilana naa ni imuse ni ẹrọ ipinlẹ ipari, ipinlẹ kọọkan eyiti o jẹ iduro fun ọgbọn kan ti sisẹ soso.
Aworan ti Ipinle UDP ti o gbẹkẹle:

Imuse ti Ilana Udp Gbẹkẹle fun .Net

Pipade - ni ko gan a ipinle, o jẹ a ibere ati opin ojuami fun automaton. Fun ipinle Pipade Àkọsílẹ iṣakoso gbigbe ti gba, eyiti, imuse olupin UDP asynchronous, gbe awọn apo-iwe ranṣẹ si awọn asopọ ti o yẹ ati bẹrẹ sisẹ ipinlẹ.

FirstPacketSending – ipo ibẹrẹ ninu eyiti asopọ ti njade jẹ nigbati ifiranṣẹ ba firanṣẹ.

Ni ipo yii, apo akọkọ fun awọn ifiranṣẹ deede ni a firanṣẹ. Fun awọn ifiranṣẹ laisi idaniloju ifiranšẹ, eyi ni ipo nikan nibiti o ti fi gbogbo ifiranṣẹ ranṣẹ.

FifiranṣẹCycle - ipo ilẹ fun gbigbe awọn apo-iwe ifiranṣẹ.

Iyipada si o lati ipinle FirstPacketSending Ti gbe jade lẹhin ti apo akọkọ ti ifiranṣẹ ti firanṣẹ. O wa ni ipo yii pe gbogbo awọn ijẹrisi ati awọn ibeere fun awọn atungbejade wa. Jade kuro ninu rẹ ṣee ṣe ni awọn ọran meji - ni ọran ti ifijiṣẹ aṣeyọri ti ifiranṣẹ tabi nipasẹ akoko ipari.

FirstPacket Ti gba – ipo ibẹrẹ fun olugba ti ifiranṣẹ naa.

O ṣayẹwo deede ti ibẹrẹ ti gbigbe, ṣẹda awọn ẹya pataki, ati firanṣẹ ifọwọsi gbigba ti apo akọkọ.

Fun ifiranṣẹ kan ti o ni apo-iwe kan ti o firanṣẹ laisi lilo ẹri ti ifijiṣẹ, eyi nikan ni ipinlẹ. Lẹhin ṣiṣe iru ifiranṣẹ kan, asopọ ti wa ni pipade.

Ijọpọ - ipilẹ ipinle fun gbigba awọn apo-iwe ifiranṣẹ.

O kọ awọn apo-iwe si ibi ipamọ igba diẹ, sọwedowo fun pipadanu apo, firanṣẹ awọn ijẹwọ fun ifijiṣẹ ti awọn apo-iwe ati gbogbo ifiranṣẹ, ati firanṣẹ awọn ibeere fun irapada awọn apo-iwe ti o sọnu. Ni ọran ti gbigba aṣeyọri ti gbogbo ifiranṣẹ, asopọ naa lọ sinu ipinlẹ naa Ti pari, bibẹẹkọ, akoko ipari kan jade.

Ti pari – pipade awọn asopọ ni irú ti aseyori ọjà ti gbogbo ifiranṣẹ.

Ipo yii jẹ pataki fun apejọ ti ifiranṣẹ naa ati fun ọran naa nigbati ijẹrisi ifijiṣẹ ti ifiranṣẹ naa ti sọnu ni ọna si olufiranṣẹ. Yi ipinle ti wa ni jade nipa akoko kan, ṣugbọn awọn asopọ ti wa ni ka ni ifijišẹ ni pipade.

Jinle sinu koodu. gbigbe Iṣakoso kuro

Ọkan ninu awọn eroja pataki ti UDP Gbẹkẹle jẹ bulọki iṣakoso gbigbe. Iṣẹ-ṣiṣe ti bulọọki yii ni lati tọju awọn asopọ lọwọlọwọ ati awọn eroja iranlọwọ, pinpin awọn apo-iwe ti nwọle si awọn asopọ ti o baamu, pese wiwo fun fifiranṣẹ awọn apo-iwe si asopọ, ati imuse API Ilana naa. Àkọsílẹ iṣakoso gbigbe gba awọn apo-iwe lati Layer UDP ati siwaju wọn si ẹrọ ipinle fun sisẹ. Lati gba awọn apo-iwe, o ṣe imuse olupin UDP asynchronous.
Diẹ ninu awọn ọmọ ẹgbẹ ti ReliableUdpConnectionControlBlock kilasi:

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

Imuse ti olupin UDP asynchronous:

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

Fun gbigbe ifiranṣẹ kọọkan, a ṣẹda eto ti o ni alaye ninu nipa asopọ. Iru eto ni a npe ni igbasilẹ asopọ.
Diẹ ninu awọn ọmọ ẹgbẹ ti kilasi 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;
  //...
}

Jinle sinu koodu. awọn ipinlẹ

Awọn ipinlẹ ṣe imuse ẹrọ ipinlẹ ti Ilana UDP Gbẹkẹle, nibiti iṣelọpọ akọkọ ti awọn apo-iwe ti waye. Kilasi áljẹbrà ReliableUdpState n pese wiwo fun ipinlẹ naa:

Imuse ti Ilana Udp Gbẹkẹle fun .Net

Gbogbo ọgbọn ti ilana naa jẹ imuse nipasẹ awọn kilasi ti a gbekalẹ loke, papọ pẹlu kilasi iranlọwọ ti o pese awọn ọna aimi, gẹgẹbi, fun apẹẹrẹ, kikọ akọsori ReliableUdp lati igbasilẹ asopọ.

Nigbamii ti, a yoo ronu ni awọn alaye imuse ti awọn ọna wiwo ti o pinnu awọn algoridimu ipilẹ ti ilana naa.

DisposeByTimeout ọna

Ọna DisposeByTimeout jẹ iduro fun itusilẹ awọn orisun asopọ lẹhin igbati o ti pẹ ati fun ifihan agbara ifiranšẹ aṣeyọri/aṣeyọri.
GbẹkẹleUdpState.DisposeByTimeout:

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

O ti bori nikan ni ipinle Ti pari.
Pari.SọnuNipaAago:

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

IlanaPackets Ọna

Ọna ProcessPackets jẹ iduro fun sisẹ afikun ti package tabi awọn idii. Ti a npe ni taara tabi nipasẹ aago idaduro apo.

Lagbara Ijọpọ awọn ọna ti wa ni overridden ati ki o jẹ lodidi fun yiyewo fun sọnu awọn apo-iwe ati awọn iyipada si ipinle Ti pari, ni ọran gbigba apo-iwe ti o kẹhin ati ṣiṣe ayẹwo aṣeyọri
Apejọ.Ilana Awọn apo-iwe:

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

Lagbara FifiranṣẹCycle ọna yi ni a npe ni nikan lori aago, ati ki o jẹ lodidi fun a resending awọn ti o kẹhin ifiranṣẹ, bi daradara bi muu awọn asopọ sunmọ aago.
FifiranṣẹCycle.ProcessPackets:

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

Lagbara Ti pari ọna naa da aago ṣiṣiṣẹ duro ati fi ifiranṣẹ ranṣẹ si awọn alabapin.
Ti pari.Ilana Awọn apo-iwe:

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

Ọna gbigbaPacket

Lagbara FirstPacket Ti gba Iṣẹ akọkọ ti ọna naa ni lati pinnu boya apo ifiranšẹ akọkọ ti de ni wiwo gangan, ati lati gba ifiranṣẹ kan ti o wa ninu apo kan.
Paaki akọkọ Ti gba.Packet Ti gba:

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

Lagbara FifiranṣẹCycle ọna yii ti bori lati gba awọn ijẹwọ ifijiṣẹ ati awọn ibeere atunkọ.
FifiranṣẹCycle.Packet.Gbigba:

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

Lagbara Ijọpọ ni ọna Gbigbawọle, iṣẹ akọkọ ti apejọ ifiranṣẹ kan lati awọn apo-iwe ti nwọle waye.
Apejọ.Packet Gbigba:

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

Lagbara Ti pari iṣẹ-ṣiṣe nikan ti ọna naa ni lati firanṣẹ tun-jẹwọ ti ifijiṣẹ aṣeyọri ti ifiranṣẹ naa.
Pari.Packet Gbigba:

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

Firanṣẹ Packet Ọna

Lagbara FirstPacketSending ọna yii firanṣẹ apo-iwe akọkọ ti data, tabi ti ifiranṣẹ ko ba nilo ijẹrisi ifijiṣẹ, gbogbo ifiranṣẹ naa.
Paake akọkọ Fifiranṣẹ.Packet

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

Lagbara FifiranṣẹCycle ni yi ọna, a Àkọsílẹ ti awọn apo-iwe ti wa ni rán.
FifiranṣẹCycle.Packet:

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

Jinle sinu koodu. Ṣiṣẹda ati Igbekale Awọn isopọ

Ni bayi ti a ti rii awọn ipinlẹ ipilẹ ati awọn ọna ti a lo lati mu awọn ipinlẹ, jẹ ki a fọ ​​awọn apẹẹrẹ diẹ ti bii ilana naa ṣe n ṣiṣẹ ni awọn alaye diẹ sii.
Aworan gbigbe data labẹ awọn ipo deede:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Ro ni apejuwe awọn ẹda igbasilẹ asopọ lati sopọ ati firanṣẹ apo-iwe akọkọ. Gbigbe naa jẹ ipilẹṣẹ nigbagbogbo nipasẹ ohun elo ti o pe ifiranšẹ API. Nigbamii ti, ọna StartTransmission ti bulọki iṣakoso gbigbe ni a pe, eyiti o bẹrẹ gbigbe data fun ifiranṣẹ tuntun naa.
Ṣiṣẹda asopọ ti njade:

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

Fifiranṣẹ apo-iwe akọkọ (ipinlẹ 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);
}

Lẹhin fifiranṣẹ apo-iwe akọkọ, olufiranṣẹ wọ inu ipinlẹ naa FifiranṣẹCycle – duro fun ìmúdájú ti package ifijiṣẹ.
Ẹgbẹ gbigba, ni lilo ọna Ipari Gbigba, gba apo-iwe ti a firanṣẹ, ṣẹda tuntun kan igbasilẹ asopọ o si kọja apo-iwe yii, pẹlu akọsori ti a ti sọ tẹlẹ, si ọna GbigbaPacket ti ipinlẹ fun sisẹ FirstPacket Ti gba
Ṣiṣẹda asopọ ni ẹgbẹ gbigba:

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

Gbigba apo-iwe akọkọ ati fifiranṣẹ iwe-ẹri (Ipinlẹ Ti gba FirstPacket):

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

Jinle sinu koodu. Pipade asopọ ni akoko ipari

Mimu akoko akoko jẹ apakan pataki ti UDP Gbẹkẹle. Wo apẹẹrẹ kan ninu eyiti ipade agbedemeji ti kuna ati ifijiṣẹ data ni awọn itọnisọna mejeeji ko ṣee ṣe.
Aworan atọka fun pipade asopọ nipasẹ akoko ipari:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Gẹgẹbi a ti le rii lati aworan atọka, aago iṣẹ olufiranṣẹ bẹrẹ lẹsẹkẹsẹ lẹhin fifiranṣẹ bulọọki awọn apo-iwe kan. Eyi ṣẹlẹ ni ọna SendPacket ti ipinle FifiranṣẹCycle.
Muu aago iṣẹ ṣiṣẹ (Fifiranṣẹ ipo Yiyi):

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

Awọn akoko aago ti ṣeto nigbati asopọ ba ṣẹda. Akoko kukuru kukuru jẹ iṣẹju-aaya 5. Ni apẹẹrẹ, o ṣeto si awọn aaya 1,5.

Fun asopọ ti nwọle, aago bẹrẹ lẹhin gbigba apo data ti nwọle ti o kẹhin, eyi ṣẹlẹ ni ọna Gbigbawọle ti ipinlẹ. Ijọpọ
Muu aago iṣẹ ṣiṣẹ (Ipinlẹ Ipejọpọ):

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

Ko si awọn apo-iwe diẹ sii ti o de lori asopọ ti nwọle lakoko ti o nduro aago iṣẹ. Aago naa lọ o si pe ọna ProcessPackets, nibiti a ti rii awọn apo-iwe ti o sọnu ati awọn ibeere irapada ti firanṣẹ fun igba akọkọ.
Fifiranṣẹ awọn ibeere irapada (ipo apejọ):

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

Oniyipada TimeSecondTry ti ṣeto si otitọ. Oniyipada yii jẹ iduro fun tun aago iṣẹ bẹrẹ.

Ni ẹgbẹ olufiranṣẹ, aago iṣiṣẹ naa tun jẹ okunfa ati apo-iwe ti o firanṣẹ kẹhin jẹ resent.
Nṣiṣẹ aago asopọ isunmọ (Fifiranṣẹ ipo Yiyi):

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

Lẹhin iyẹn, aago asopọ isunmọ bẹrẹ ni asopọ ti njade.
GbẹkẹleUdpState.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);
}

Asopo akoko aago akoko isunmọ jẹ iṣẹju-aaya 30 nipasẹ aiyipada.

Lẹhin igba diẹ, aago ti n ṣiṣẹ ni ẹgbẹ olugba yoo tun jo, awọn ibeere ni a firanṣẹ lẹẹkansi, lẹhinna aago isunmọ asopọ bẹrẹ fun asopọ ti nwọle.

Nigbati awọn aago isunmọ ina, gbogbo awọn orisun ti awọn igbasilẹ asopọ mejeeji jẹ idasilẹ. Olufiranṣẹ ṣe ijabọ ikuna ifijiṣẹ si ohun elo oke (wo UDP API Gbẹkẹle).
Tusilẹ awọn orisun igbasilẹ asopọ:

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

Jinle sinu koodu. mimu-pada sipo data gbigbe

Aworan imularada gbigbe data ni ọran ti pipadanu soso:Imuse ti Ilana Udp Gbẹkẹle fun .Net

Gẹgẹbi a ti sọrọ tẹlẹ ni pipade asopọ ni akoko ipari, nigbati aago iṣẹ ba pari, olugba yoo ṣayẹwo fun awọn apo-iwe ti o sọnu. Ni ọran ti ipadanu soso, atokọ ti nọmba awọn apo-iwe ti ko de ọdọ olugba yoo ṣe akojọpọ. Awọn nọmba wọnyi ti wa ni titẹ si ọna LostPackets ti asopọ kan pato, ati awọn ibeere fun irapada ni a firanṣẹ.
Fifiranṣẹ awọn ibeere si awọn akojọpọ idapada (Ipo Npejọ):

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

Olufiranṣẹ yoo gba ibeere irapada ati firanṣẹ awọn apo-iwe ti o padanu. O tọ lati ṣe akiyesi pe ni akoko yii olufiranṣẹ ti bẹrẹ aago isunmọ asopọ ati pe, nigbati o ba gba ibeere kan, o ti tunto.
Nfi awọn apo-iwe ti o sọnu pada (Fifiranṣẹ ipinlẹCycle):

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

Awọn apo-iwe resent (packet #3 ninu aworan atọka) jẹ gbigba nipasẹ asopọ ti nwọle. A ṣayẹwo lati rii boya window gbigba ti kun ati pe gbigbe data deede ti pada.
Ṣiṣayẹwo fun awọn deba ni ferese gbigba (ipinlẹ apejọ):

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

UDP API ti o gbẹkẹle

Lati ṣe ajọṣepọ pẹlu ilana gbigbe data, kilasi Udp Gbẹkẹle ti o ṣii, eyiti o jẹ iwe-ipamọ lori bulọọki iṣakoso gbigbe. Eyi ni awọn ọmọ ẹgbẹ pataki julọ ti kilasi naa:

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

Awọn ifiranṣẹ gba nipasẹ ṣiṣe alabapin. Ibuwọlu aṣoju fun ọna ipepada:

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

Ifiranṣẹ:

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

Lati ṣe alabapin si iru ifiranṣẹ kan pato ati/tabi olufiranṣẹ kan pato, awọn aye iyan meji ni a lo: ReliableUdpMessageTypes messageType ati IPEndPoint ipEndPoint.

Awọn iru ifiranṣẹ:

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

Ifiranṣẹ naa ni a firanṣẹ ni asynchronously; fun eyi, ilana naa ṣe imuse awoṣe siseto asynchronous:

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

Abajade ti fifiranṣẹ ifiranṣẹ yoo jẹ otitọ - ti ifiranṣẹ ba de ọdọ olugba ni aṣeyọri ati eke - ti asopọ ba ti wa ni pipade nipasẹ akoko ipari:

public bool EndSendMessage(IAsyncResult asyncResult)

ipari

Pupọ ko ti ṣe apejuwe ninu nkan yii. Awọn ọna ṣiṣe ibaramu okun, imukuro ati mimu aṣiṣe, imuse awọn ọna fifiranṣẹ asynchronous. Ṣugbọn ipilẹ ti ilana naa, apejuwe ti ọgbọn-ọrọ fun awọn apo-iwe sisẹ, iṣeto asopọ, ati mimu awọn akoko ipari, yẹ ki o han si ọ.

Ẹya ti a ṣe afihan ti Ilana ifijiṣẹ igbẹkẹle jẹ logan ati rọ to lati pade awọn ibeere asọye tẹlẹ. Ṣugbọn Mo fẹ lati ṣafikun pe imuse ti a ṣalaye le ni ilọsiwaju. Fun apẹẹrẹ, lati mu iṣelọpọ pọsi ati awọn akoko akoko iyipada ni agbara, awọn ọna bii window sisun ati RTT le ṣafikun si ilana naa, yoo tun wulo lati ṣe ilana kan fun ṣiṣe ipinnu MTU laarin awọn apa asopọ (ṣugbọn nikan ti awọn ifiranṣẹ nla ba firanṣẹ) .

O ṣeun fun akiyesi rẹ, Mo nireti awọn asọye ati awọn asọye rẹ.

PS Fun awọn ti o nifẹ si awọn alaye tabi o kan fẹ lati ṣe idanwo ilana naa, ọna asopọ si iṣẹ akanṣe lori GitHube:
Gbẹkẹle UDP ise agbese

Wulo ìjápọ ati ìwé

  1. Sipesifikesonu Ilana TCP: ni ede Gẹẹsi и ni ede Russian
  2. UDP Ilana sipesifikesonu: ni ede Gẹẹsi и ni ede Russian
  3. Ifọrọwanilẹnuwo ti Ilana RUDP: draft-ietf-sigtran-reliable-udp-00
  4. Ilana Data Gbẹkẹle: Rfc 908 и Rfc 1151
  5. Imuse ti o rọrun ti ijẹrisi ifijiṣẹ lori UDP: Gba Iṣakoso Lapapọ ti Nẹtiwọọki rẹ Pẹlu .NET Ati UDP
  6. Nkan ti n ṣapejuwe awọn ọna gbigbe NAT: Ibaraẹnisọrọ Ẹlẹgbẹ-si-Ẹgbẹ Kọja Awọn Onitumọ Adirẹsi Nẹtiwọọki
  7. Imuse ti awoṣe siseto asynchronous: Ṣiṣe Awoṣe Eto Asynchronous CLR и Bii o ṣe le ṣe ilana apẹrẹ IAsyncResult
  8. Gbigbe awoṣe siseto asynchronous si apẹrẹ asynchronous ti o da lori iṣẹ-ṣiṣe (APM ni TAP):
    TPL ati Ibile .NET Asynchronous siseto
    Interop pẹlu Awọn Ilana Asynchronous miiran ati Awọn oriṣi

Imudojuiwọn: O ṣeun Mayorovp и sidristij fun imọran ti fifi iṣẹ-ṣiṣe kun si wiwo. Ibamu ti ile-ikawe pẹlu awọn ọna ṣiṣe atijọ ko ṣẹ, nitori Ilana 4th ṣe atilẹyin mejeeji XP ati olupin 2003.

orisun: www.habr.com

Fi ọrọìwòye kun