Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Efa niova hatry ny ela ny Internet. Iray amin'ireo protocols lehibe amin'ny Internet - UDP dia ampiasain'ny fampiharana tsy amin'ny fandefasana datagrams sy fampielezam-peo ihany, fa koa mba hanomezana fifandraisana "peer-to-peer" eo amin'ny node tambajotra. Noho ny endrika tsotra, ity protocole ity dia manana fampiasana maro tsy nomanina teo aloha, na izany aza, ny tsy fahampian'ny protocol, toy ny tsy fisian'ny fanaterana azo antoka, dia tsy nanjavona na aiza na aiza. Ity lahatsoratra ity dia mamaritra ny fampiharana ny protocole fanaterana azo antoka amin'ny UDP.
Hevitra ato Anatiny:teny
Fepetra takian'ny protocole
Lohateny UDP azo itokisana
Fitsipika ankapoben'ny protocole
Timeouts sy timers protocole
Sarin'ny fanjakana fifindran'ny UDP azo itokisana
Lalina kokoa amin'ny code. rafitra fanaraha-maso ny fifindran'ny
Lalina kokoa amin'ny code. Hoy ny

Lalina kokoa amin'ny code. Famoronana sy fametrahana fifandraisana
Lalina kokoa amin'ny code. Manakatona ny fifandraisana rehefa tapitra ny fotoana
Lalina kokoa amin'ny code. Famerenana ny famindrana angona
API UDP azo itokisana
famaranana
Rohy mahasoa sy lahatsoratra

teny

Ny maritrano tany am-boalohany an'ny Internet dia nihevitra ny habaka adiresy homogene izay misy ny node tsirairay manana adiresy IP manerantany sy tokana ary afaka mifandray mivantana amin'ny nodes hafa. Ankehitriny ny Internet, raha ny marina, dia manana maritrano hafa - faritra iray misy adiresy IP manerantany ary faritra maro misy adiresy manokana miafina ao ambadiky ny fitaovana NAT.Amin'ity maritrano ity, ny fitaovana ao amin'ny habaka adiresy manerantany ihany no afaka mifandray mora amin'ny olona rehetra ao amin'ny tambajotra satria manana adiresy IP tokana azo alefa maneran-tany izy ireo. Ny node iray amin'ny tambajotra tsy miankina dia afaka mifandray amin'ny node hafa amin'ny tambajotra iray ihany, ary afaka mifandray amin'ny node hafa malaza ao amin'ny habaka adiresy manerantany. Ity fifaneraserana ity dia tena azo atao noho ny fomba fandikana ny adiresin'ny tambajotra. Ny fitaovana NAT, toy ny router Wi-Fi, dia mamorona fidirana an-databatra fandikan-teny manokana ho an'ny fifandraisana mivoaka ary manova ny adiresy IP sy ny laharan'ny seranana ao anaty fonosana. Izany dia ahafahan'ny fifandraisana mivoaka avy amin'ny tambajotra manokana mankany amin'ny mpampiantrano ao amin'ny habaka adiresy manerantany. Saingy miaraka amin'izay koa, matetika ny fitaovana NAT dia manakana ny fifamoivoizana miditra rehetra raha tsy misy fitsipika manokana momba ny fifandraisana miditra.

Ity maritrano amin'ny Internet ity dia mety tsara ho an'ny fifandraisana amin'ny mpanjifa-server, izay ahafahan'ny mpanjifa miditra amin'ny tambajotra tsy miankina, ary manana adiresy manerantany ny mpizara. Saingy miteraka fahasarotana amin'ny fifandraisana mivantana amin'ny nodes roa eo anelanelany isan-karazany tambajotra tsy miankina. Ny fifandraisana mivantana eo amin'ny node roa dia zava-dehibe ho an'ny fampiharana peer-to-peer toy ny fampitana feo (Skype), fahazoana fidirana lavitra amin'ny solosaina (TeamViewer), na lalao an-tserasera.

Ny iray amin'ireo fomba mahomby indrindra amin'ny fametrahana fifandraisana amin'ny mpiara-miasa eo amin'ny fitaovana amin'ny tambajotra tsy miankina samihafa dia antsoina hoe hole punching. Ity teknika ity dia matetika ampiasaina amin'ny fampiharana mifototra amin'ny protocol UDP.

Fa raha mitaky fanaterana angon-drakitra azo antoka ny fampiharanao, ohatra, mamindra rakitra eo amin'ny solosaina ianao, dia hanana fahasahiranana maro ny fampiasana UDP noho ny zava-misy fa ny UDP dia tsy protocole fanaterana azo antoka ary tsy manome ny fandefasana fonosana araka ny filaharany, fa tsy toy ny TCP protocol.

Amin'ity tranga ity, mba hiantohana ny fandefasana fonosana azo antoka, ilaina ny mampihatra protocol layer fampiharana izay manome ny fiasa ilaina ary miasa amin'ny UDP.

Te-hanamarika avy hatrany aho fa misy teknika fanokanana lavaka TCP amin'ny fametrahana fifandraisana TCP eo amin'ny nodes amin'ny tambajotra tsy miankina samihafa, saingy noho ny tsy fahampian'ny fanohanan'ny fitaovana NAT maro, dia matetika no tsy heverina ho fomba lehibe hampifandraisana azy io. nodes toy izany.

Ho an'ny ambiny amin'ity lahatsoratra ity dia hifantoka amin'ny fampiharana ny protocole fanaterana azo antoka aho. Holazaina ao amin'ny lahatsoratra manaraka ny fampiharana ny teknikan'ny fanosehana lavaka UDP.

Fepetra takian'ny protocole

  1. Ny fanaterana fonosana azo itokisana dia nampiharina tamin'ny alàlan'ny mekanika fanehoan-kevitra tsara (ilay antsoina hoe fanekena tsara)
  2. Ilaina ny famindrana angon-drakitra lehibe, i.e. ny protocol dia tsy maintsy misoroka ny fampitana fonosana tsy ilaina
  3. Tokony ho azo atao ny manafoana ny rafitra fanamafisana ny fanaterana (ny fahafahana miasa toy ny protocol UDP "madio")
  4. Fahaizana mampihatra ny fomba baiko, miaraka amin'ny fanamafisana ny hafatra tsirairay
  5. Ny singa fototra amin'ny famindrana angon-drakitra amin'ny protocol dia tsy maintsy hafatra

Ireo fepetra ireo dia mifanandrify indrindra amin'ny fepetra takian'ny Protocol Data Reliable voalaza ao rfc 908 и rfc 1151, ary niantehitra tamin'ireo fenitra ireo aho rehefa namolavola ity protocol ity.

Mba hahatakarana ireo fepetra ireo, andeha hojerentsika ny fotoana famindrana angon-drakitra eo anelanelan'ny node tambajotra roa mampiasa ny protocols TCP sy UDP. Avelao amin'ireo tranga roa ireo dia ho very ny fonosana iray.
Famindrana angon-drakitra tsy misy fifandraisana amin'ny TCP:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Araka ny hitanao amin'ny kisary, raha sendra very ny fonosana, ny TCP dia hamantatra ny fonosana very ary hitatitra izany amin'ny mpandefa amin'ny fangatahana ny isan'ny ampahany very.
Famindrana angona amin'ny alàlan'ny protocol UDP:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Ny UDP dia tsy manao dingana fitadiavana fatiantoka. Ny fanaraha-maso ny fahadisoana fampitana ao amin'ny protocol UDP dia andraikitry ny fampiharana.

Ny fisavana ny hadisoana ao amin'ny protocol TCP dia vita amin'ny alàlan'ny fametrahana fifandraisana amin'ny node farany, fitehirizana ny toetry ny fifandraisana, manondro ny isan'ny bytes alefa amin'ny lohatenin'ny fonosana tsirairay, ary ny fampahafantarana ny tapakila amin'ny alàlan'ny laharan'ny fanekena.

Fanampin'izany, mba hanatsarana ny fampandehanana (izany hoe fandefasana fizarana mihoatra ny iray nefa tsy nahazo fankasitrahana), ny protocol TCP dia mampiasa ilay antsoina hoe varavarankely fampitana - ny isan'ny bytes ny angona antenain'ny mpandefa ny fizarana.

Raha mila fanazavana fanampiny momba ny protocol TCP dia jereo rfc 793, manomboka amin'ny UDP mankany rfc 768aiza, raha ny marina, no voafaritra.

Avy amin'ireo voalaza etsy ambony ireo dia mazava fa mba hamoronana protocole fandefasana hafatra azo itokisana amin'ny UDP (antsoina hoe UDP azo itokisana), ilaina ny fampiharana ny fomba famindrana angona mitovy amin'ny TCP. izany hoe:

  • tehirizo ny fifandraisana
  • mampiasa fanisana fizarana
  • mampiasa fonosana fanamafisana manokana
  • mampiasa mekanika fikandrana tsotsotra mba hampitomboana ny fidirana protocol

Ankoatra izany, mila:

  • famantarana ny fanombohan'ny hafatra, mba hizara loharanon-karena ho an'ny fifandraisana
  • famantarana ny fiafaran'ny hafatra, hampita ny hafatra voaray amin'ny fampiharana ambony ary hamoaka loharanon'ny protocol
  • avelao ny protocole manokana amin'ny fifandraisana hanaisotra ny rafitra fanamafisana fanaterana mba hiasa ho UDP "madio".

Lohateny UDP azo itokisana

Tsarovy fa ny datagram UDP dia voarakitra ao anaty datagrama IP. Ny fonosana UDP azo itokisana dia "voafono" amin'ny datagrama UDP.
Encapsulation lohatenin'ny UDP azo itokisana:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Tsotra ny firafitry ny lohatenin'ny UDP azo itokisana:

Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

  • Flags - saina mifehy ny fonosana
  • MessageType - karazana hafatra ampiasain'ny fampiharana ambony mba hisoratra anarana amin'ny hafatra manokana
  • TransmissionId - ny laharan'ny fampitana, miaraka amin'ny adiresy sy seranan-tsambon'ny mpandray, dia manondro manokana ny fifandraisana
  • PacketNumber - packet number
  • Safidy - safidy protocol fanampiny. Raha ny fonosana voalohany dia ampiasaina hanondroana ny haben'ny hafatra

Ny saina dia toy izao manaraka izao:

  • FirstPacket - ny fonosana voalohany amin'ny hafatra
  • NoAsk - ny hafatra dia tsy mitaky mekanika fanekena ho alefa
  • LastPacket - ny fonosana farany amin'ny hafatra
  • RequestForPacket - fonosana fanamafisana na fangatahana fonosana very

Fitsipika ankapoben'ny protocole

Satria ny UDP azo itokisana dia mifantoka amin'ny fampitana hafatra azo antoka eo anelanelan'ny node roa, dia tsy maintsy afaka mametraka fifandraisana amin'ny ilany iray izy. Mba hametrahana fifandraisana dia mandefa fonosana miaraka amin'ny saina FirstPacket ny mpandefa, ny valiny dia midika fa ny fifandraisana dia napetraka. Ny fonosana valim-panontaniana rehetra, na, amin'ny teny hafa, ny fonosana fankasitrahana, dia mametraka foana ny sandan'ny saha PacketNumber ho iray mihoatra ny sandan'ny PacketNumber lehibe indrindra amin'ny fonosana voaray. Ny saha Options ho an'ny fonosana voalohany nalefa dia ny haben'ny hafatra.

Mekanisma mitovy amin'izany no ampiasaina hamarana ny fifandraisana. Ny saina LastPacket dia napetraka amin'ny fonosana farany amin'ny hafatra. Ao amin'ny fonosan'ny valinteny dia aseho ny laharan'ny fonosana farany + 1, izay midika ho fampitaovana mahomby ny hafatra ho an'ny lafiny fandraisana.
Fametrahana fifandraisana sy kisary famaranana:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Rehefa tafapetraka ny fifandraisana dia manomboka ny famindrana angon-drakitra. Ny angona dia alefa amin'ny blocs of packets. Ny sakana tsirairay, afa-tsy ny farany, dia misy fonosana maromaro. Mitovy amin'ny haben'ny fikandrana mandray/mandefa izany. Ny sakana farany amin'ny angona dia mety ho vitsy kokoa ny fonosana. Aorian'ny fandefasana ny sakana tsirairay dia miandry ny fanamafisana ny fandefasana na fangatahana hamerenana indray ny fonosana very ny lafiny fandefasana, ka mamela ny varavarankely fandraisana/fampitana misokatra mba hahazoana valiny. Taorian'ny nahazoana fanamafisana ny fanaterana sakana, dia miova ny varavarankelin'ny fandraisana/fampitana ary alefa ny sakana manaraka.

Ny lafiny mpandray dia mandray ny fonosana. Ny fonosana tsirairay dia voamarina raha toa ka tafiditra ao anatin'ny varavarankely fampitana. Ny fonosana sy ny dika mitovy izay tsy latsaka ao anaty varavarankely dia voasivana. SATRIA Raha toa ka raikitra ny haben'ny varavarankely ary mitovy amin'ny mpandray sy ny mpandefa, dia raha misy sakana misy fonosana tsy misy fatiantoka, dia afindra ny varavarankely mba handraisana ny fonosana amin'ny sakana manaraka ary ny fanamafisana ny fandefasana dia nalefa. Raha tsy feno ny varavarankely ao anatin'ny fe-potoana voatondron'ny fameram-potoana miasa, dia hatomboka ny fisavana izay mbola tsy naterina ny fonosana ary halefa ny fangatahana famerenana.
Diagrama fandefasana indray:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Timeouts sy timers protocole

Misy antony maromaro tsy ahafahana mametraka fifandraisana. Ohatra, raha ivelan'ny aterineto ny mpandray anjara. Amin'ity tranga ity, rehefa miezaka manangana fifandraisana, dia hikatona ny fifandraisana rehefa tapitra ny fotoana. Ny fampiharana UDP azo ianteherana dia mampiasa fameram-potoana roa mba hametrahana fe-potoana. Ny voalohany, ny fameram-potoana miasa, dia ampiasaina hiandry valiny avy amin'ny mpampiantrano lavitra. Raha mirehitra eo amin'ny lafiny mpandefa ilay izy, dia alefa indray ilay fonosana nalefa farany. Raha lany ny fameram-potoana ao amin'ny mpandray, dia atao ny fanamarinana ny fonosana very ary alefa ny fangatahana fanaterana.

Ny fameram-potoana faharoa dia ilaina hanidy ny fifandraisana raha sendra tsy fahampian'ny fifandraisana eo amin'ireo nodes. Ho an'ny lafiny mpandefa dia manomboka avy hatrany rehefa tapitra ny fameram-potoana miasa, ary miandry valiny avy amin'ny node lavitra. Raha tsy misy valiny ao anatin'ny fe-potoana voafaritra dia tapaka ny fifandraisana ary avoaka ny loharano. Ho an'ny lafiny fandraisana, manomboka ny fameram-potoana akaiky ny fifandraisana rehefa tapitra indroa ny fameram-potoana miasa. Ilaina izany mba hiantohana ny fahaverezan'ny fonosana fanamafisana. Rehefa tapitra ny fameram-potoana dia tapaka ihany koa ny fifandraisana ary mivoaka ny loharano.

Sarin'ny fanjakana fifindran'ny UDP azo itokisana

Ny fitsipiky ny protocol dia ampiharina amin'ny milina fanjakana voafetra, ny fanjakana tsirairay dia tompon'andraikitra amin'ny lojika sasany amin'ny fanodinana fonosana.
Diagramm-panjakana UDP azo itokisana:

Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

mihidy - tsy tena fanjakana fa toerana fanombohana sy fiafaran'ny automatique. Ho an’ny fanjakana mihidy voaray ny sakana fanaraha-maso ny fandefasana, izay, amin'ny fampiharana ny mpizara UDP asynchronous, dia mandefa ny fonosana amin'ny fifandraisana mety ary manomboka ny fanodinana fanjakana.

FirstPacketSending – ny fanjakana voalohany misy ny fifandraisana mivoaka rehefa alefa ny hafatra.

Amin'ity fanjakana ity dia alefa ny fonosana voalohany ho an'ny hafatra mahazatra. Ho an'ny hafatra tsy misy fanamafisana ny fandefasana dia io ihany no fanjakana handefasana ny hafatra manontolo.

SendingCycle – toerana ifotony amin'ny fampitana ny fonosana hafatra.

Tetezamita ho azy avy amin'ny fanjakana FirstPacketSending natao taorian'ny nandefasana ny fonosana voalohany amin'ny hafatra. Ao amin'io fanjakana io no tonga ny fanekena sy ny fangatahana fampitana indray. Ny fivoahana amin'izany dia azo atao amin'ny tranga roa - raha toa ka mahomby ny fandefasana ny hafatra na amin'ny fe-potoana.

FirstPacketReceived - ny fanjakana voalohany ho an'ny mpandray ny hafatra.

Izy io dia manamarina ny fahamarinan'ny fiandohan'ny fampitana, mamorona ny rafitra ilaina, ary mandefa fanekena ny fahazoana ny fonosana voalohany.

Ho an'ny hafatra iray misy fonosana tokana ary nalefa tsy nampiasa porofo momba ny fandefasana dia io ihany no fanjakana. Rehefa vita ny fanodinana hafatra toy izany dia mikatona ny fifandraisana.

mivory - fanjakana fototra handraisana ny fonosan'ny hafatra.

Izy io dia manoratra fonosana ho any amin'ny fitahirizana vonjimaika, manamarina ny fahaverezan'ny fonosana, mandefa fankasitrahana amin'ny fandefasana andiana fonosana sy ny hafatra iray manontolo, ary mandefa fangatahana hamerenana ny fonosana very. Raha toa ka mahomby ny fandraisana ny hafatra manontolo, ny fifandraisana dia miditra amin'ny fanjakana vita, raha tsy izany dia hivoaka ny fe-potoana.

vita – fanakatonana ny fifandraisana raha toa ka nahomby ny fandraisana ny hafatra manontolo.

Ity fanjakana ity dia ilaina amin'ny fivorian'ny hafatra sy amin'ny tranga raha very ny fanamafisana ny fandefasana ny hafatra teny an-dalana mankany amin'ny mpandefa. Ity fanjakana ity dia miala amin'ny fe-potoana, fa ny fifandraisana dia heverina ho nikatona soa aman-tsara.

Lalina kokoa amin'ny code. rafitra fanaraha-maso ny fifindran'ny

Ny iray amin'ireo singa manan-danja amin'ny UDP azo itokisana dia ny sakana fanaraha-maso ny fifindrana. Ny asan'ity sakana ity dia ny mitahiry ny fifandraisana misy ankehitriny sy ny singa fanampiny, mizara ny fonosana miditra amin'ny fifandraisana mifanaraka amin'izany, manome interface tsara handefasana fonosana amin'ny fifandraisana, ary mampihatra ny protocol API. Ny sakana fanaraha-maso ny fifindrana dia mandray fonosana avy amin'ny sosona UDP ary mandefa azy ireo any amin'ny milina fanjakana ho an'ny fanodinana. Mba hahazoana packet dia mampiasa mpizara UDP asynchronous izy.
Ny mpikambana sasany ao amin'ny kilasy 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;    	
  //...
}

Fampiharana ny mpizara 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);
}

Ho an'ny famindrana hafatra tsirairay dia misy rafitra iray mirakitra fampahalalana momba ny fifandraisana. Ny rafitra toy izany dia antsoina hoe firaketana fifandraisana.
Ny mpikambana sasany ao amin'ny kilasy 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;
  //...
}

Lalina kokoa amin'ny code. Hoy ny

Ny fanjakana dia mampihatra ny milinam-panjakana amin'ny protocol UDP Reliable, izay misy ny fanodinana lehibe ny fonosana. Ny kilasy abstract ReliableUdpState dia manome interface tsara ho an'ny fanjakana:

Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Ny lojika manontolo amin'ny protocol dia ampiharina amin'ny kilasy aseho etsy ambony, miaraka amin'ny kilasy fanampiny izay manome fomba static, toy ny, ohatra, ny fananganana ny lohatenin'ny ReliableUdp avy amin'ny firaketana fifandraisana.

Manaraka, dia handinika amin'ny an-tsipiriany ny fampiharana ny fomba interface tsara izay mamaritra ny fototry ny algorithms ny protocol.

DisposeByTimeout Method

Ny fomba DisposeByTimeout dia tompon'andraikitra amin'ny famoahana ireo loharanom-pifandraisana aorian'ny fe-potoana ary amin'ny famantarana ny fandefasana hafatra mahomby/tsy nahomby.
ReliableUdpState.DisposeByTimeout:

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

Any amin’ny fanjakana ihany no amboarina vita.
Vita.DisposeByTimeout:

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

Fomba ProcessPackets

Ny fomba ProcessPackets dia tompon'andraikitra amin'ny fanodinana fanampiny amin'ny fonosana na fonosana. Antsoina mivantana na amin'ny alàlan'ny fameram-potoana fiandrasana fonosana.

afaka mivory ny fomba dia nosoloina ary tompon'andraikitra amin'ny fanamarinana ny fonosana very sy ny fifindrana mankany amin'ny fanjakana vita, raha toa ka nahazo ny fonosana farany ary nandalo fanamarinana mahomby
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);
  }
}

afaka SendingCycle Ity fomba ity dia antsoina amin'ny fameram-potoana ihany, ary tompon'andraikitra amin'ny fandefasana ny hafatra farany, ary koa ny fampandehanana ny fameram-potoana akaiky ny fifandraisana.
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);
}

afaka vita Ny fomba dia manakana ny fampandehanana fotoana ary mandefa ny hafatra ho an'ny mpanjifa.
Vita.ProcessPackets:

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

ReceivePacket fomba

afaka FirstPacketReceived Ny tena asa ny fomba dia ny hamaritana raha tonga teo amin'ny interface tsara ny fonosana hafatra voalohany, ary koa ny fanangonana hafatra misy fonosana tokana.
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);
  }
}

afaka SendingCycle io fomba io dia nosoloina mba hanaiky ny fanekena fanaterana sy ny fangatahana fandefasana indray.
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));
}

afaka mivory amin'ny fomba ReceivePacket, ny asa lehibe amin'ny fanangonana hafatra avy amin'ny fonosana miditra dia mitranga.
Assembling.ReceivePacket:

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

afaka vita Ny hany andraikitry ny fomba dia ny mandefa indray ny fanekena ny fahombiazan'ny fandefasana ny hafatra.
Vita. ReceivePacket:

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

Fomba fandefasana fonosana

afaka FirstPacketSending Ity fomba ity dia mandefa ny fonosana voalohany misy angon-drakitra, na raha tsy mila fanamafisana ny fandefasana ny hafatra, dia ny hafatra manontolo.
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);
}

afaka SendingCycle Amin'ity fomba ity, misy andian-kitapo alefa.
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 );
  }
}

Lalina kokoa amin'ny code. Famoronana sy fametrahana fifandraisana

Ankehitriny rehefa nahita ny fanjakana fototra sy ny fomba ampiasaina amin'ny fitantanana fanjakana isika, andao hanazava ohatra vitsivitsy momba ny fomba fiasan'ny protocol amin'ny antsipiriany bebe kokoa.
Diagram fampitana angon-drakitra ao anatin'ny fepetra mahazatra:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Diniho amin'ny an-tsipiriany ny famoronana firaketana fifandraisana mampifandray sy mandefa ny fonosana voalohany. Ny famindrana dia atomboka hatrany amin'ny fampiharana izay miantso ny fandefasana hafatra API. Avy eo, ny fomba StartTransmission amin'ny sakana fanaraha-maso ny fifindrana dia antsoina, izay manomboka ny fandefasana angon-drakitra ho an'ny hafatra vaovao.
Mamorona fifandraisana mivoaka:

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

Mandefa ny fonosana voalohany (FirstPacketSending state):

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

Aorian'ny fandefasana ny fonosana voalohany dia miditra amin'ny fanjakana ny mpandefa SendingCycle - miandry ny fanamafisana ny fandefasana entana.
Ny lafiny fandraisana, mampiasa ny fomba EndReceive, dia mandray ny fonosana nalefa, mamorona vaovao firaketana fifandraisana ary ampita ity fonosana ity, miaraka amin'ny lohapejy efa voaomana mialoha, mankany amin'ny fomba ReceivePacket an'ny fanjakana ho an'ny fanodinana FirstPacketReceived
Mamorona fifandraisana amin'ny lafiny fandraisana:

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

Mandray ny fonosana voalohany ary mandefa fanekena (FirstPacketReceived fanjakana):

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

Lalina kokoa amin'ny code. Manakatona ny fifandraisana rehefa tapitra ny fotoana

Ampahany manan-danja ao amin'ny UDP azo itokisana ny fitantanana ny fotoana. Diniho ohatra iray izay tsy nahomby ny node mpanelanelana ary nanjary tsy azo atao ny fandefasana angona amin'ny lafiny roa.
Diagram amin'ny fanakatonana fifandraisana amin'ny fe-potoana:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Araka ny hita amin'ny kisary dia manomboka avy hatrany ny fameram-potoana fiasan'ny mpandefa rehefa avy nandefa andiana fonosana. Izany dia mitranga amin'ny fomba SendPacket an'ny fanjakana SendingCycle.
Fampandehanana ny fameram-potoana (fanjakana SendingCycle):

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

Ny fe-potoana fameram-potoana dia napetraka rehefa noforonina ny fifandraisana. Ny ShortTimerPeriod default dia 5 segondra. Ao amin'ny ohatra, dia napetraka ho 1,5 segondra.

Ho an'ny fifandraisana miditra, manomboka ny fameram-potoana aorian'ny fandraisana ny packet data miditra farany, izany dia mitranga amin'ny fomba ReceivePacket an'ny fanjakana. mivory
Fampandehanana ny fameram-potoana (fanjakana fanangonana):

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

Tsy nisy fonosana tonga teo amin'ny fifandraisana tonga teo am-piandrasana ny fameram-potoana miasa. Nandeha ny fameram-potoana ary niantso ny fomba ProcessPackets, izay nahitana ireo fonosana very ary nalefa voalohany ny fangatahana famerenana.
Fandefasana fangatahana fanaterana indray (fanjakana fanangonana):

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

Ny variable TimerSecondTry dia napetraka amin'ny marina. Ity faribolana ity dia tompon'andraikitra amin'ny famerenana ny fameram-potoana miasa.

Eo amin'ny sisin'ny mpandefa, ny fameram-potoana miasa ihany koa dia alefa ary alefa indray ny fonosana nalefa farany.
Mandeha ny famerana akaiky ny fifandraisana (fanjakana SendingCycle):

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

Aorian'izay dia manomboka amin'ny fifandraisana mivoaka ny fameram-potoana akaiky ny fifandraisana.
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);
}

Ny fe-potoana fiatoan'ny fameram-potoana fanakatonana dia 30 segondra raha ny mahazatra.

Rehefa afaka fotoana fohy, dia mirehitra indray ny fameram-potoana miasa eo amin'ny lafiny mpandray, alefa indray ny fangatahana, aorian'izay dia manomboka ny famerana akaiky ny fifandraisana ho an'ny fifandraisana miditra.

Rehefa mirehitra ny fameram-potoana akaiky, dia avoaka daholo ny loharanon'ny firaketana an-tsoratra roa. Ny mpandefa dia mitatitra ny tsy fahombiazan'ny fandefasana amin'ny fampiharana ambony (jereo UDP API azo itokisana).
Famoahana loharanon-drakitra momba ny fifandraisana:

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

Lalina kokoa amin'ny code. Famerenana ny famindrana angona

Diagram fanarenana fampitana angon-drakitra raha sendra very ny fonosana:Fampiharana ny protocol Udp azo itokisana ho an'ny .Net

Araka ny efa noresahina teo amin'ny fanakatonana ny fifandraisana amin'ny fiafaran'ny fotoana, rehefa tapitra ny fameram-potoana miasa, dia hanamarina ny entana very ny mpandray. Raha sanatria very packet dia hangonina ny lisitry ny isan'ny packet tsy tonga any amin'ny mpandray. Ireo isa ireo dia ampidirina ao amin'ny laharan'ny LostPackets amin'ny fifandraisana manokana, ary alefa ny fangatahana fanaterana.
Fandefasana fangatahana hanaterana indray ny fonosana (fanjakana mitambatra):

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

Hanaiky ny fangatahana fanaterana indray ny mpandefa ary handefa ny fonosana tsy hita. Tsara ny manamarika fa amin'izao fotoana izao ny mpandefa dia efa nanomboka ny fifandraisana akaiky timer ary, rehefa misy fangatahana voaray, dia reset.
Mandefa indray ny fonosana very (fanjakana SendingCycle):

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

Ny fonosana resent (fonosana #3 amin'ny kisary) dia raisina amin'ny fifandraisana miditra. Ny fanamarinana dia atao mba hahitana raha feno ny varavarankely fandraisana ary haverina amin'ny laoniny ny fandefasana angon-drakitra.
Fanamarinana ireo hitsaoka ao amin'ny varavarankely fandraisana (Fanjakana fanangonana):

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 azo itokisana

Mba hifaneraserana amin'ny protocol transfer data dia misy kilasy Udp azo itokisana misokatra, izay fonosina amin'ny sakana fanaraha-maso ny famindrana. Ireto ny mpikambana manan-danja indrindra amin'ny kilasy:

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

Ny hafatra dia raisina amin'ny alàlan'ny famandrihana. Alefaso sonia ho an'ny fomba fiantsoana:

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

Сообщение:

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

Raha te hisoratra anarana amin'ny karazana hafatra manokana sy/na mpandefa manokana, dia misy masontsivana roa azo ampiasaina: ReliableUdpMessageTypes messageType sy IPEndPoint ipEndPoint.

Karazana hafatra:

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

Ny hafatra dia alefa asynchronous; noho izany, ny protocol dia mametraka modely fandaharana asynchronous:

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

Ny valin'ny fandefasana hafatra dia ho marina - raha toa ka tonga soa aman-tsara any amin'ny mpandray ny hafatra ary diso - raha nikatona ny fifandraisana tamin'ny fe-potoana:

public bool EndSendMessage(IAsyncResult asyncResult)

famaranana

Be dia be no tsy voalaza ato amin'ity lahatsoratra ity. Mekanisma mifanandrify amin'ny kofehy, maningana sy fitantanana ny fahadisoana, fampiharana ny fomba fandefasana hafatra asynchronous. Fa ny fototry ny protocole, ny famaritana ny lojika ho an'ny fanodinana fonosana, ny fametrahana fifandraisana, ary ny fikarakarana ny fe-potoana, dia tokony ho mazava aminao.

Ny dikan-teny nasehon'ny protocole fanaterana azo itokisana dia matanjaka sy azo leferina ampy hahafeno ireo fepetra efa voafaritra teo aloha. Saingy tiako ny manampy fa azo hatsaraina ny fampiharana voalaza. Ohatra, mba hampitomboana ny fampandehanana sy hanovana ny vanim-potoanan'ny fameram-potoana, dia azo ampidirina amin'ny protocol ny mekanika toy ny fikandrana sliding sy ny RTT, ilaina ihany koa ny fampiharana mekanika hamaritana ny MTU eo anelanelan'ny node fifandraisana (fa raha misy hafatra lehibe alefa) .

Misaotra anao amin'ny fiheveranao, manantena ny fanehoan-kevitra sy ny fanehoan-kevitrao aho.

PS Ho an'ireo izay liana amin'ny antsipiriany na te hanandrana ny protocol fotsiny, ny rohy mankany amin'ny tetikasa ao amin'ny GitHube:
Tetikasa UDP azo itokisana

Rohy mahasoa sy lahatsoratra

  1. Famaritana ny protocol TCP: amin'ny teny anglisy и amin'ny teny rosiana
  2. Famaritana ny protocol UDP: amin'ny teny anglisy и amin'ny teny rosiana
  3. Resadresaka momba ny protocol RUDP: draft-ietf-signtran-reliable-udp-00
  4. Protocol Data azo antoka: rfc 908 и rfc 1151
  5. Fampiharana tsotra ny fanamafisana fanaterana amin'ny UDP: Raiso ny fifehezana tanteraka ny tambajotranao miaraka amin'ny .NET sy UDP
  6. Lahatsoratra mamaritra ny mekanika fandalovan'ny NAT: Fifandraisana Peer-to-Peer Manerana ny Mpandika Adiresin'ny Tambajotra
  7. Fampiharana ny modely fandaharana asynchronous: Mampihatra ny CLR Asynchronous Programming Model и Ahoana ny fampiharana ny lamina famolavolana IAsyncResult
  8. Fandefasana ny maodely fandaharana asynchronous amin'ny lamina asynchronous mifototra amin'ny asa (APM amin'ny TAP):
    TPL sy Traditional .NET Asynchronous Programming
    Mifandraika amin'ny lamina sy karazana asynchronous hafa

Fanavaozana: Misaotra mayorovp и sidristij ho an'ny hevitra hanampiana asa amin'ny interface. Ny mifanaraka amin'ny tranomboky amin'ny rafitra miasa taloha dia tsy voahitsakitsaka, satria Ny rafitra faha-4 dia manohana ny mpizara XP sy 2003.

Source: www.habr.com

Add a comment