Pagpatuman sa Kasaligang Udp protocol para sa .Net

Ang Internet dugay nang nausab. Usa sa mga nag-unang protocol sa Internet - UDP gigamit sa mga aplikasyon dili lamang sa paghatud sa mga datagrams ug mga sibya, apan usab sa paghatag og "peer-to-peer" nga mga koneksyon tali sa network nodes. Tungod sa yano nga disenyo niini, kini nga protocol adunay daghan nga wala'y plano nga paggamit, bisan pa, ang mga kakulangan sa protocol, sama sa kakulang sa garantiya nga paghatud, wala mawala bisan asa. Kini nga artikulo naghulagway sa pagpatuman sa garantiya nga delivery protocol sa UDP.
Mga Kaundan:entry
Mga Kinahanglanon sa Protokol
Kasaligan nga UDP header
Kinatibuk-ang mga prinsipyo sa protocol
Mga timeout ug protocol timers
Kasaligan nga UDP transmission state diagram
Lalim pa sa code. yunit sa pagkontrol sa transmission
Lalim pa sa code. estado

Lalim pa sa code. Paghimo ug Pagtukod og mga Koneksyon
Lalim pa sa code. Pagsira sa koneksyon sa timeout
Lalim pa sa code. Pag-uli sa pagbalhin sa datos
Kasaligang UDP API
konklusyon
Mapuslanon nga mga link ug mga artikulo

entry

Ang orihinal nga arkitektura sa Internet miangkon sa usa ka homogenous address space diin ang matag node adunay usa ka global ug talagsaon nga IP address ug direktang makigkomunikar sa ubang mga node. Karon ang Internet, sa tinuud, adunay lahi nga arkitektura - usa ka lugar sa mga global nga IP address ug daghang mga lugar nga adunay mga pribadong adres nga gitago sa luyo sa mga aparato sa NAT.Niini nga arkitektura, ang mga himan lamang sa global address space ang dali nga makigkomunikar sa bisan kinsa sa network tungod kay sila adunay talagsaon, globally routable IP address. Ang usa ka node sa usa ka pribado nga network mahimong magkonektar sa ubang mga node sa parehas nga network, ug mahimo usab nga magkonektar sa ubang mga ilado nga node sa global address space. Kini nga interaksyon makab-ot tungod sa mekanismo sa paghubad sa adres sa network. Ang NAT device, sama sa Wi-Fi routers, naghimo ug espesyal nga translation table entries para sa outgoing connections ug nag-usab sa mga IP address ug port number sa mga packet. Gitugotan niini ang mga outgoing connections gikan sa pribadong network ngadto sa mga host sa global address space. Apan sa parehas nga oras, ang mga aparato sa NAT kasagarang nagbabag sa tanan nga umaabot nga trapiko gawas kung gitakda ang lahi nga mga lagda alang sa umaabot nga mga koneksyon.

Kini nga arkitektura sa Internet igo nga husto alang sa komunikasyon sa kliyente-server, diin ang mga kliyente mahimong naa sa mga pribadong network, ug ang mga server adunay usa ka global nga adres. Apan nagmugna kini og mga kalisud alang sa direktang koneksyon sa duha ka mga node tali lainlain pribadong network. Ang direktang koneksyon tali sa duha ka node importante para sa peer-to-peer nga mga aplikasyon sama sa voice transmission (Skype), pagkuha og remote access sa computer (TeamViewer), o online gaming.

Usa sa labing epektibo nga mga pamaagi alang sa pag-establisar sa usa ka peer-to-peer nga koneksyon tali sa mga himan sa lain-laing mga pribadong network gitawag nga hole punching. Kini nga teknik kasagarang gigamit sa mga aplikasyon base sa UDP protocol.

Apan kung ang imong aplikasyon nanginahanglan garantiya nga paghatud sa mga datos, pananglitan, imong gibalhin ang mga file sa taliwala sa mga kompyuter, nan ang paggamit sa UDP adunay daghang mga kalisud tungod sa kamatuoran nga ang UDP dili usa ka garantiya nga protocol sa pagpadala ug wala maghatag sa paghatud sa packet sa han-ay, dili sama sa TCP protocol.

Sa kini nga kaso, aron masiguro ang garantiya nga paghatud sa packet, gikinahanglan nga ipatuman ang usa ka protocol sa layer sa aplikasyon nga naghatag sa kinahanglan nga pagpaandar ug molihok sa UDP.

Gusto nakong timan-an dayon nga adunay TCP hole punching technique alang sa pag-establisar sa mga koneksyon sa TCP tali sa mga node sa lain-laing mga pribadong network, apan tungod sa kakulang sa suporta niini sa daghang NAT device, kasagaran dili kini isipon nga pangunang paagi sa pagkonektar. ingon nga mga node.

Alang sa nahabilin sa kini nga artikulo, magpunting lamang ako sa pagpatuman sa garantiya nga protocol sa pagpadala. Ang pagpatuman sa UDP hole punching technique ihulagway sa mosunod nga mga artikulo.

Mga Kinahanglanon sa Protokol

  1. Ang kasaligan nga pagpadala sa pakete nga gipatuman pinaagi sa usa ka positibo nga mekanismo sa feedback (ang gitawag nga positibo nga pag-ila)
  2. Ang panginahanglan alang sa episyente nga pagbalhin sa dagkong datos, i.e. ang protocol kinahanglang maglikay sa wala kinahanglana nga packet relaying
  3. Mahimong posible nga kanselahon ang mekanismo sa pagkumpirma sa pagpadala (ang abilidad nga molihok ingon usa ka "puro" nga protocol sa UDP)
  4. Abilidad sa pagpatuman sa command mode, uban sa pagkumpirma sa matag mensahe
  5. Ang sukaranan nga yunit sa pagbalhin sa datos sa protocol kinahanglan usa ka mensahe

Kini nga mga kinahanglanon kadaghanan nahiuyon sa mga kinahanglanon nga Kasaligang Data Protocol nga gihulagway sa RFC 908 и RFC 1151, ug nagsalig ko sa mga sumbanan sa paghimo niini nga protocol.

Aron masabtan kini nga mga kinahanglanon, atong tan-awon ang timing sa pagbalhin sa datos tali sa duha ka network nodes gamit ang TCP ug UDP protocols. Himoa nga sa duha ka mga kaso kita adunay usa ka pakete nga nawala.
Pagbalhin sa non-interactive nga data sa TCP:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Sama sa imong makita gikan sa diagram, sa kaso sa pagkawala sa packet, ang TCP makamatikod sa nawala nga packet ug i-report kini sa nagpadala pinaagi sa pagpangutana sa numero sa nawala nga bahin.
Pagbalhin sa datos pinaagi sa UDP protocol:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Ang UDP wala maghimo bisan unsang mga lakang sa pag-ila sa pagkawala. Ang pagpugong sa mga sayup sa transmission sa protocol sa UDP hingpit nga responsibilidad sa aplikasyon.

Ang pag-ila sa sayup sa TCP protocol makab-ot pinaagi sa pag-establisar og koneksyon sa usa ka end node, pagtipig sa estado sa maong koneksyon, nga nagpakita sa gidaghanon sa mga byte nga gipadala sa matag packet header, ug pagpahibalo sa mga resibo gamit ang numero sa pag-ila.

Dugang pa, aron mapauswag ang pasundayag (sama sa pagpadala labaw sa usa ka bahin nga wala makadawat usa ka pag-ila), ang protocol sa TCP naggamit sa gitawag nga window sa transmission - ang gidaghanon sa mga byte sa datos nga gilauman nga madawat sa nagpadala sa bahin.

Alang sa dugang nga impormasyon bahin sa TCP protocol, tan-awa RFC 793, gikan sa UDP ngadto sa RFC 768diin, sa pagkatinuod, sila gihubit.

Gikan sa ibabaw, klaro nga aron makahimo usa ka kasaligan nga protocol sa paghatud sa mensahe sa UDP (gitawag nga Masaligan nga UDP), gikinahanglan nga ipatuman ang mga mekanismo sa pagbalhin sa datos susama sa TCP. Nga mao:

  • i-save ang kahimtang sa koneksyon
  • gamita ang segment numbering
  • gamita ang mga espesyal nga pakete sa pagkumpirma
  • gamita ang gipasimple nga mekanismo sa pag-windowing aron madugangan ang pag-agi sa protocol

Dugang pa, kinahanglan nimo:

  • signal sa pagsugod sa usa ka mensahe, sa paggahin mga kapanguhaan alang sa koneksyon
  • signal ang katapusan sa usa ka mensahe, aron ipasa ang nadawat nga mensahe sa upstream nga aplikasyon ug buhian ang mga kapanguhaan sa protocol
  • tugoti ang protocol nga espesipiko sa koneksyon sa pag-disable sa mekanismo sa pagkumpirma sa pagpadala nga molihok isip "puro" nga UDP

Kasaligan nga UDP header

Hinumdomi nga ang usa ka UDP datagram gisulod sa usa ka IP datagram. Ang Kasaligang UDP packet tukma nga "giputos" sa usa ka UDP datagram.
Kasaligan nga UDP header encapsulation:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Ang istruktura sa Masaligan nga UDP header yano ra:

Pagpatuman sa Kasaligang Udp protocol para sa .Net

  • Mga bandera - mga bandera sa pagkontrol sa pakete
  • MessageType - tipo sa mensahe nga gigamit sa upstream nga mga aplikasyon aron mag-subscribe sa piho nga mga mensahe
  • TransmissionId - ang numero sa transmission, kauban ang adres ug pantalan sa nakadawat, talagsaon nga nagpaila sa koneksyon
  • PacketNumber - numero sa pakete
  • Mga kapilian - dugang nga mga kapilian sa protocol. Sa kaso sa unang pakete, kini gigamit sa pagpakita sa gidak-on sa mensahe

Ang mga bandera mao ang mosunod:

  • FirstPacket - ang unang pakete sa mensahe
  • NoAsk - ang mensahe wala magkinahanglan og mekanismo sa pag-ila aron ma-enable
  • LastPacket - ang katapusang pakete sa mensahe
  • RequestForPacket - pakete sa pagkumpirma o paghangyo alang sa nawala nga pakete

Kinatibuk-ang mga prinsipyo sa protocol

Tungod kay ang Kasaligang UDP naka-focus sa garantisadong pagpadala sa mensahe tali sa duha ka mga node, kinahanglan nga makahimo kini og koneksyon sa pikas kilid. Aron matukod ang usa ka koneksyon, ang nagpadala nagpadala usa ka pakete nga adunay bandila sa FirstPacket, ang tubag nga nagpasabut nga ang koneksyon natukod. Ang tanan nga mga pakete sa tubag, o, sa laing pagkasulti, mga pakete sa pag-ila, kanunay nga nagbutang sa kantidad sa field sa PacketNumber ngadto sa usa nga labaw pa sa pinakadako nga kantidad sa PacketNumber sa malampuson nga nadawat nga mga pakete. Ang Options field para sa unang packet nga gipadala mao ang gidak-on sa mensahe.

Ang usa ka susama nga mekanismo gigamit sa pagtapos sa usa ka koneksyon. Ang LastPacket nga bandila gibutang sa katapusan nga pakete sa mensahe. Sa tubag nga pakete, ang gidaghanon sa katapusang pakete + 1 gipakita, nga alang sa pagdawat nga bahin nagpasabut nga malampuson nga paghatud sa mensahe.
Pagtukod sa koneksyon ug diagram sa pagtapos:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Kung natukod ang koneksyon, magsugod ang pagbalhin sa data. Ang datos gipasa sa mga bloke sa mga pakete. Ang matag bloke, gawas sa katapusan, adunay usa ka piho nga gidaghanon sa mga pakete. Parehas kini sa gidak-on sa pagdawat/pagpadala sa bintana. Ang katapusan nga bloke sa datos mahimong adunay gamay nga pakete. Human ipadala ang matag block, ang nagpadala nga bahin maghulat alang sa usa ka kumpirmasyon sa pagpadala o usa ka hangyo sa paghatud pag-usab sa nawala nga mga pakete, gibiyaan ang pagdawat / pagpadala nga bukas aron makadawat mga tubag. Human madawat ang kumpirmasyon sa paghatod sa block, ang receive/transmit window shifts ug ang sunod nga block sa data ipadala.

Ang nagdawat nga bahin makadawat sa mga pakete. Ang matag pakete gisusi aron makita kung kini nahulog sa sulud sa transmission window. Ang mga pakete ug mga duplicate nga dili mahulog sa bintana gisala. Kay Kung ang gidak-on sa bintana gitakda ug parehas alang sa nakadawat ug nagpadala, nan sa kaso sa usa ka bloke sa mga pakete nga gihatag nga wala’y pagkawala, ang bintana gibalhin aron makadawat mga pakete sa sunod nga bloke sa datos ug usa ka kumpirmasyon sa pagpadala gipadala. Kung ang bintana dili mapuno sa sulod sa panahon nga gitakda sa timer sa trabaho, unya ang usa ka tseke magsugod kung diin ang mga pakete wala pa mapadala ug ang mga hangyo alang sa pagpadala pag-usab ipadala.
Retransmission Diagram:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Mga timeout ug protocol timers

Adunay daghang mga hinungdan ngano nga ang usa ka koneksyon dili ma-establisar. Pananglitan, kung offline ang nakadawat nga partido. Sa kini nga kaso, kung mosulay sa pag-establisar og koneksyon, ang koneksyon pagasirhan sa timeout. Ang Kasaligang pagpatuman sa UDP naggamit ug duha ka mga timer aron magtakda og mga timeout. Ang una, ang working timer, gigamit sa paghulat sa tubag gikan sa remote host. Kung kini nagdilaab sa kilid sa nagpadala, nan ang katapusan nga gipadala nga pakete nasuko. Kung ang timer mo-expire sa nakadawat, unya ang usa ka tseke alang sa nawala nga mga packet ipahigayon ug ang mga hangyo alang sa redelivery ipadala.

Ang ikaduha nga timer gikinahanglan aron masira ang koneksyon kung adunay kakulang sa komunikasyon tali sa mga node. Alang sa sender nga bahin, magsugod dayon kini human matapos ang working timer, ug maghulat alang sa tubag gikan sa hilit nga node. Kung walay tubag sulod sa gitakda nga panahon, ang koneksyon mahunong ug ang mga kahinguhaan ipagawas. Alang sa pagdawat nga bahin, ang koneksyon close timer gisugdan human ang work timer matapos sa makaduha. Gikinahanglan kini aron maseguro batok sa pagkawala sa pakete sa pagkumpirma. Sa diha nga ang timer matapos, ang koneksyon natapos usab ug ang mga kapanguhaan gibuhian.

Kasaligan nga UDP transmission state diagram

Ang mga prinsipyo sa protocol gipatuman sa usa ka limitado nga makina sa estado, ang matag estado nga responsable sa usa ka piho nga lohika sa pagproseso sa pakete.
Kasaligang UDP State Diagram:

Pagpatuman sa Kasaligang Udp protocol para sa .Net

Sirado - dili gyud usa ka estado, kini usa ka pagsugod ug katapusan nga punto alang sa automaton. Para sa estado Sirado ang usa ka transmission control block nadawat, nga, nga nagpatuman sa usa ka asynchronous nga UDP server, nagpasa sa mga packet ngadto sa angay nga mga koneksyon ug nagsugod sa pagproseso sa estado.

UnangPacketSending – ang inisyal nga kahimtang diin ang mogawas nga koneksyon mao ang pagpadala sa mensahe.

Niini nga estado, ang unang pakete alang sa normal nga mga mensahe gipadala. Alang sa mga mensahe nga walay kumpirmasyon sa pagpadala, kini lamang ang estado diin ang tibuok mensahe gipadala.

SendingCycle – ground state para sa pagpasa sa mga pakete sa mensahe.

Pagbalhin ngadto niini gikan sa estado UnangPacketSending gihimo human mapadala ang unang pakete sa mensahe. Niini nga kahimtang nga ang tanan nga mga pag-ila ug mga hangyo alang sa retransmissions moabut. Ang paggawas gikan niini posible sa duha ka mga kaso - sa kaso sa malampuson nga paghatod sa mensahe o pinaagi sa timeout.

UnangPacketNadawat – ang inisyal nga kahimtang alang sa nakadawat sa mensahe.

Gisusi niini ang pagkahusto sa pagsugod sa pagpasa, paghimo sa gikinahanglan nga mga istruktura, ug pagpadala sa usa ka pag-ila sa pagkadawat sa unang pakete.

Alang sa usa ka mensahe nga naglangkob sa usa ka pakete ug gipadala nga wala gigamit ang pruweba sa pagpadala, kini ra ang estado. Pagkahuman sa pagproseso sa ingon nga mensahe, ang koneksyon sirado.

Pagpundok - sukaranan nga estado alang sa pagdawat sa mga pakete sa mensahe.

Kini nagsulat sa mga packet ngadto sa temporaryo nga pagtipig, nagsusi sa packet loss, nagpadala sa mga pag-ila alang sa paghatod sa usa ka block sa mga packet ug sa tibuok nga mensahe, ug nagpadala sa mga hangyo alang sa redelivery sa nawala nga mga packet. Sa kaso sa malampuson nga pagdawat sa tibuok nga mensahe, ang koneksyon moadto sa estado nahuman, kon dili, ang usa ka timeout mogawas.

nahuman – pagsira sa koneksyon sa kaso sa malampuson nga pagdawat sa tibuok mensahe.

Kini nga kahimtang gikinahanglan alang sa asembliya sa mensahe ug alang sa kaso kung ang pagkumpirma sa pagpadala sa mensahe nawala sa dalan sa nagpadala. Kini nga estado mogawas pinaagi sa usa ka timeout, apan ang koneksyon giisip nga malampuson nga sirado.

Lalim pa sa code. yunit sa pagkontrol sa transmission

Usa sa mga yawe nga elemento sa Kasaligang UDP mao ang transmission control block. Ang tahas niini nga block mao ang pagtipig sa kasamtangan nga mga koneksyon ug mga elemento sa auxiliary, pag-apod-apod sa umaabot nga mga pakete ngadto sa katugbang nga mga koneksyon, paghatag og interface alang sa pagpadala sa mga pakete ngadto sa usa ka koneksyon, ug pagpatuman sa protocol API. Ang transmission control block makadawat og mga packet gikan sa UDP layer ug ipasa kini ngadto sa state machine para sa pagproseso. Aron makadawat og mga packet, nagpatuman kini og asynchronous nga UDP server.
Pipila ka mga miyembro sa ReliableUdpConnectionControlBlock nga klase:

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

Pagpatuman sa asynchronous nga UDP server:

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

Alang sa matag pagbalhin sa mensahe, usa ka istruktura ang gihimo nga adunay sulud nga kasayuran bahin sa koneksyon. Ang ingon nga istruktura gitawag rekord sa koneksyon.
Pipila ka mga miyembro sa ReliableUdpConnectionRecord nga klase:

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

Lalim pa sa code. estado

Gipatuman sa mga estado ang makina sa estado sa Kasaligang UDP protocol, diin ang nag-unang pagproseso sa mga pakete mahitabo. Ang abstract nga klase nga ReliableUdpState naghatag usa ka interface alang sa estado:

Pagpatuman sa Kasaligang Udp protocol para sa .Net

Ang tibuuk nga lohika sa protocol gipatuman sa mga klase nga gipresentar sa ibabaw, kauban ang usa ka auxiliary nga klase nga naghatag mga static nga pamaagi, sama sa, pananglitan, pagtukod sa ReliableUdp header gikan sa rekord sa koneksyon.

Sunod, atong hisgotan sa detalye ang pagpatuman sa mga pamaagi sa interface nga nagtino sa sukaranang mga algorithm sa protocol.

DisposeByTimeout nga pamaagi

Ang DisposeByTimeout nga pamaagi maoy responsable sa pagpagawas sa mga kahinguhaan sa koneksyon human sa usa ka timeout ug sa pagsenyas sa malampuson/dili malampuson nga paghatud sa mensahe.
KasaliganUdpState.DisposeByTimeout:

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

Gi-override lang kini sa estado nahuman.
Nakompleto.DisposeByTimeout:

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

Pamaagi sa ProcessPackets

Ang ProcessPackets nga pamaagi maoy responsable sa dugang nga pagproseso sa usa ka pakete o mga pakete. Direkta nga gitawag o pinaagi sa packet wait timer.

Makahimo Pagpundok ang pamaagi gi-override ug responsable sa pagsusi sa nawala nga mga pakete ug pagbalhin sa estado nahuman, sa kaso sa pagdawat sa katapusang packet ug pagpasa sa malampuson nga tseke
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);
  }
}

Makahimo SendingCycle kini nga pamaagi gitawag lamang sa usa ka timer, ug responsable sa pagpadala pag-usab sa katapusang mensahe, ingon man usab sa pagpagana sa koneksyon close timer.
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);
}

Makahimo nahuman ang pamaagi mohunong sa running timer ug ipadala ang mensahe ngadto sa mga subscriber.
Nakompleto.ProcessPackets:

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

Pamaagi sa ReceivePacket

Makahimo UnangPacketNadawat ang nag-unang tahas sa pamaagi mao ang pagtino kung ang una nga pakete sa mensahe nakaabut ba gyud sa interface, ug usab pagkolekta usa ka mensahe nga gilangkuban sa usa ka pakete.
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);
  }
}

Makahimo SendingCycle kini nga pamaagi gi-overridden sa pagdawat sa delivery acknowledgement ug retransmission requests.
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));
}

Makahimo Pagpundok sa ReceivePacket nga pamaagi, ang nag-unang buluhaton sa pag-assemble sa usa ka mensahe gikan sa umaabot nga mga pakete mahitabo.
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);
  }
}

Makahimo nahuman ang bugtong tahas sa pamaagi mao ang pagpadala pag-usab sa pag-ila sa malampuson nga paghatod sa mensahe.
Nakompleto.ReceivePacket:

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

Ipadala ang Packet Method

Makahimo UnangPacketSending kini nga pamaagi nagpadala sa unang packet sa data, o kung ang mensahe wala magkinahanglan ug delivery confirmation, ang tibuok mensahe.
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);
}

Makahimo SendingCycle sa niini nga paagi, usa ka bloke sa mga pakete gipadala.
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 );
  }
}

Lalim pa sa code. Paghimo ug Pagtukod og mga Koneksyon

Karon nga nakita na nato ang mga batakang estado ug ang mga pamaagi nga gigamit sa pagdumala sa mga estado, atong gub-on ang pipila ka mga pananglitan kon sa unsang paagi ang protocol molihok sa mas detalyado.
Data transmission diagram ubos sa normal nga kondisyon:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Tagda sa detalye ang paglalang rekord sa koneksyon aron makonektar ug ipadala ang unang pakete. Ang pagbalhin kanunay nga gisugdan sa aplikasyon nga nagtawag sa ipadala nga mensahe nga API. Sunod, ang StartTransmission nga pamaagi sa transmission control block gihangyo, nga nagsugod sa pagpadala sa datos alang sa bag-ong mensahe.
Paghimo og outgoing connection:

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

Pagpadala sa unang packet (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);
}

Human ipadala ang unang pakete, ang nagpadala mosulod sa estado SendingCycle – maghulat alang sa kumpirmasyon sa pagpadala sa package.
Ang nagdawat nga bahin, gamit ang EndReceive nga pamaagi, nakadawat sa gipadala nga pakete, nagmugna og bag-o rekord sa koneksyon ug ipasa kini nga pakete, nga adunay pre-parsed header, ngadto sa ReceivePacket nga pamaagi sa estado alang sa pagproseso UnangPacketNadawat
Paghimo og koneksyon sa nakadawat nga bahin:

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

Pagdawat sa unang pakete ug pagpadala sa usa ka pag-ila (FirstPacketReceived estado):

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

Lalim pa sa code. Pagsira sa koneksyon sa timeout

Ang pagdumala sa timeout usa ka importante nga bahin sa Kasaligang UDP. Tagda ang usa ka pananglitan diin ang usa ka intermediate node napakyas ug ang paghatud sa datos sa duha ka direksyon nahimong imposible.
Diagram para sa pagsira sa koneksyon pinaagi sa timeout:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Sama sa makita gikan sa diagram, ang oras sa trabaho sa nagpadala magsugod dayon pagkahuman ipadala ang usa ka bloke sa mga pakete. Kini mahitabo sa SendPacket nga pamaagi sa estado SendingCycle.
Pag-enable sa work timer (SendingCycle state):

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

Ang mga panahon sa timer gitakda kung ang koneksyon gihimo. Ang default nga ShortTimerPeriod kay 5 segundos. Sa pananglitan, gitakda kini sa 1,5 segundos.

Alang sa umaabot nga koneksyon, ang timer magsugod human madawat ang katapusang umaabot nga data packet, kini mahitabo sa ReceivePacket nga pamaagi sa estado. Pagpundok
Pag-enable sa work timer (Assembling state):

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

Wala nay mga pakete nga miabot sa umaabot nga koneksyon samtang naghulat sa nagtrabaho nga timer. Ang timer nawala ug gitawag ang ProcessPackets nga pamaagi, diin ang nawala nga mga pakete nakit-an ug ang mga hangyo sa pagpadala gipadala sa unang higayon.
Pagpadala og mga hangyo sa pagpadala (Asembling state):

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

Ang TimerSecondTry variable gitakda sa tinuod nga. Kini nga variable ang responsable sa pag-restart sa working timer.

Sa kiliran sa nagpadala, ang working timer ma-trigger usab ug ang kataposang gipadala nga packet gi-resent.
Pag-enable sa koneksyon close timer (SendingCycle state):

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

Human niana, ang koneksyon close timer magsugod sa outgoing koneksyon.
KasaliganUdpState.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);
}

Ang koneksyon close timer timeout period kay 30 segundos isip default.

Pagkahuman sa mubo nga panahon, ang nagtrabaho nga timer sa kilid sa nakadawat nagdilaab pag-usab, ang mga hangyo gipadala pag-usab, pagkahuman ang koneksyon nga close timer magsugod alang sa umaabot nga koneksyon

Kung ang mga close timers mobuto, ang tanan nga mga kapanguhaan sa duha nga mga rekord sa koneksyon ipagawas. Ang nagpadala nagreport sa kapakyasan sa pagpadala sa upstream nga aplikasyon (tan-awa ang Kasaligang UDP API).
Pagpagawas sa mga kapanguhaan sa rekord sa koneksyon:

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

Lalim pa sa code. Pag-uli sa pagbalhin sa datos

Data transmission recovery diagram sa kaso sa packet loss:Pagpatuman sa Kasaligang Udp protocol para sa .Net

Sama sa nahisgotan na sa pagsira sa koneksyon sa timeout, sa diha nga ang nagtrabaho timer matapos, ang receiver mosusi sa nawala packets. Sa kaso sa pagkawala sa packet, usa ka lista sa gidaghanon sa mga pakete nga wala makaabot sa nakadawat ang tigumon. Kini nga mga numero gisulod sa LostPackets array sa usa ka piho nga koneksyon, ug ang mga hangyo alang sa redelivery gipadala.
Pagpadala og mga hangyo sa pagpadala pag-usab sa mga pakete (Assembling state):

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

Ang nagpadala modawat sa redelivery request ug ipadala ang nawala nga mga packet. Angay nga matikdan nga niining higayona ang nagpadala nagsugod na sa koneksyon nga close timer ug, kung ang usa ka hangyo nadawat, kini gi-reset.
Pagpadala pag-usab sa nawala nga mga pakete (SendingCycle state):

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

Ang nasuko nga pakete (packet #3 sa diagram) madawat sa umaabot nga koneksyon. Gihimo ang usa ka pagsusi aron makita kung puno ang bintana sa pagdawat ug ang normal nga transmission sa datos gipahiuli.
Pagsusi sa mga hit sa receive window (Assembling state):

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

Kasaligang UDP API

Aron makig-uban sa data transfer protocol, adunay bukas nga Kasaligang Udp nga klase, nga usa ka wrapper sa transfer control block. Ania ang labing importante nga mga sakop sa klase:

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

Ang mga mensahe madawat pinaagi sa suskrisyon. Delegado nga pirma para sa callback nga paagi:

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

Aron mag-subscribe sa usa ka piho nga tipo sa mensahe ug/o usa ka piho nga nagpadala, duha ka opsyonal nga parameter ang gigamit: ReliableUdpMessageTypes messageType ug IPEndPoint ipEndPoint.

Mga tipo sa mensahe:

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

Ang mensahe gipadala asynchronously; alang niini, ang protocol nagpatuman sa usa ka asynchronous programming model:

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

Ang resulta sa pagpadala sa usa ka mensahe tinuod - kung ang mensahe malampuson nga nakaabot sa nakadawat ug bakak - kung ang koneksyon gisirado sa timeout:

public bool EndSendMessage(IAsyncResult asyncResult)

konklusyon

Daghan ang wala mahulagway niining artikuloha. Mga mekanismo sa pagpares sa thread, eksepsiyon ug pagdumala sa sayop, pagpatuman sa asynchronous nga mga paagi sa pagpadala sa mensahe. Apan ang kinauyokan sa protocol, ang paghulagway sa lohika alang sa pagproseso sa mga pakete, pag-establisar og koneksyon, ug pagdumala sa mga timeout, kinahanglan nga klaro kanimo.

Ang gipakita nga bersyon sa kasaligan nga protocol sa pagpadala lig-on ug igo nga flexible aron matubag ang mga gipasabut kaniadto nga mga kinahanglanon. Apan gusto nakong idugang nga ang gihulagway nga pagpatuman mahimong mapauswag. Pananglitan, aron madugangan ang throughput ug dinamikong pagbag-o sa mga panahon sa timer, ang mga mekanismo sama sa sliding window ug RTT mahimong idugang sa protocol, mapuslanon usab ang pagpatuman sa usa ka mekanismo sa pagtino sa MTU tali sa mga node sa koneksyon (apan kung ipadala ang dagkong mga mensahe) .

Salamat sa imong pagtagad, nagpaabut ako sa imong mga komento ug komento.

PS Para sa mga interesado sa mga detalye o gusto lang sulayan ang protocol, ang link sa proyekto sa GitHube:
Kasaligang proyekto sa UDP

Mapuslanon nga mga link ug mga artikulo

  1. Mga detalye sa TCP protocol: sa Iningles и sa Russian
  2. Mga detalye sa protocol sa UDP: sa Iningles и sa Russian
  3. Paghisgot sa RUDP protocol: draft-ietf-sigtran-kasaligan-udp-00
  4. Kasaligang Data Protocol: RFC 908 и RFC 1151
  5. Usa ka yano nga pagpatuman sa kumpirmasyon sa pagpadala sa UDP: Kuhaa ang Total Control Sa Imong Networking Uban sa .NET Ug UDP
  6. Artikulo nga naghulagway sa NAT traversal mechanism: Komunikasyon sa Peer-to-Peer Sa Tibuok Network Address Mga Maghuhubad
  7. Pagpatuman sa asynchronous programming model: Pagpatuman sa CLR Asynchronous Programming Model и Giunsa pagpatuman ang sumbanan sa disenyo sa IAsyncResult
  8. Pagdala sa asynchronous programming model ngadto sa task-based asynchronous pattern (APM sa TAP):
    TPL ug Tradisyonal nga .NET Asynchronous Programming
    Interop sa Ubang Asynchronous nga mga Pattern ug Type

Update: Salamat mayorovp и sidristij alang sa ideya sa pagdugang usa ka buluhaton sa interface. Ang pagkaangay sa librarya sa daan nga mga operating system wala gilapas, tungod kay Ang 4th framework nagsuporta sa XP ug 2003 server.

Source: www.habr.com

Idugang sa usa ka comment