Aiwatar da Dogaran Udp yarjejeniya don .Net

Intanet ta canza tuntuni. Ɗaya daga cikin manyan ka'idoji na Intanet - UDP yana amfani da aikace-aikace ba kawai don sadar da bayanai da watsa shirye-shirye ba, amma har ma don samar da haɗin gwiwar "tsara-da-tsara" tsakanin nodes na cibiyar sadarwa. Saboda ƙirar sa mai sauƙi, wannan ƙa'idar tana da amfani da yawa a baya waɗanda ba a tsara su ba, duk da haka, gazawar ka'idar, kamar rashin garantin bayarwa, ba su ɓace ko'ina ba. Wannan labarin yana bayyana aiwatar da garantin isar da ka'idar akan UDP.
Abubuwan:Gabatarwa
Abubuwan Bukatun Protocol
Amintaccen shugaban UDP
Gabaɗaya ka'idodin yarjejeniya
Ƙayyadaddun lokaci da masu ƙidayar yarjejeniya
Dogaro da tsarin watsawa na UDP
Zurfafa cikin code. naúrar sarrafa watsawa
Zurfafa cikin code. jihohi

Zurfafa cikin code. Ƙirƙirar da Ƙirƙirar Haɗuwa
Zurfafa cikin code. Rufe haɗin kan lokacin ƙarewa
Zurfafa cikin code. Ana dawo da canja wurin bayanai
Amintaccen UDP API
ƙarshe
Hanyoyi masu amfani da labarai

Gabatarwa

Asalin gine-ginen Intanet ya ɗauki sararin adireshi iri ɗaya wanda kowane kumburi yana da adireshin IP na duniya da na musamman kuma yana iya sadarwa kai tsaye tare da sauran nodes. Yanzu Intanet, a zahiri, tana da gine-gine daban-daban - yanki ɗaya na adiresoshin IP na duniya da yankuna da yawa masu adiresoshin sirri da ke ɓoye a bayan na'urorin NAT.A cikin wannan gine-gine, na'urori kawai a cikin sararin adireshi na duniya suna iya sadarwa cikin sauƙi tare da kowa a kan hanyar sadarwa saboda suna da na musamman, adireshin IP na duniya. Kumburi a cibiyar sadarwa mai zaman kansa na iya haɗawa zuwa wasu nodes akan hanyar sadarwa ɗaya, kuma yana iya haɗawa zuwa wasu sanannun nodes a cikin sararin adireshi na duniya. Ana samun wannan hulɗar galibi saboda tsarin fassarar adireshin cibiyar sadarwa. Na'urorin NAT, kamar masu amfani da hanyar sadarwa na Wi-Fi, suna ƙirƙira shigarwar tebur na fassarar musamman don haɗin kai da canza adiresoshin IP da lambobin tashar jiragen ruwa a cikin fakiti. Wannan yana ba da damar haɗi mai fita daga cibiyar sadarwar masu zaman kansu zuwa masu ɗaukar hoto a cikin sararin adireshi na duniya. Amma a lokaci guda, na'urorin NAT galibi suna toshe duk zirga-zirgar zirga-zirgar da ke shigowa sai dai idan an saita ƙa'idodi daban-daban na haɗin shiga.

Wannan gine-ginen Intanet daidai ne don sadarwar abokin ciniki da uwar garken, inda abokan ciniki zasu iya kasancewa a cikin cibiyoyin sadarwa masu zaman kansu, kuma sabobin suna da adireshin duniya. Amma yana haifar da matsaloli ga haɗin kai tsaye na nodes biyu tsakanin daban -daban cibiyoyin sadarwa masu zaman kansu. Haɗin kai tsaye tsakanin nodes guda biyu yana da mahimmanci ga aikace-aikacen ɗan-ɗan-tsara kamar watsa murya (Skype), samun damar nesa zuwa kwamfuta (TeamViewer), ko wasan kwaikwayo na kan layi.

Ɗaya daga cikin ingantattun hanyoyin kafa haɗin kai-da-tsara tsakanin na'urori akan cibiyoyin sadarwa masu zaman kansu daban-daban shine ake kira holl punching. An fi amfani da wannan fasaha tare da aikace-aikace bisa ka'idar UDP.

Amma idan aikace-aikacen ku yana buƙatar isar da garantin isar da bayanai, misali, kuna canja wurin fayiloli tsakanin kwamfutoci, to amfani da UDP zai sami matsaloli da yawa saboda UDP ba ƙa'idar isar da garantin ba ce kuma baya samar da fakiti cikin tsari, sabanin TCP. yarjejeniya.

A wannan yanayin, don tabbatar da isar da fakitin garanti, ana buƙatar aiwatar da ƙa'idar Layer na aikace-aikacen da ke ba da aikin da ake buƙata kuma yana aiki akan UDP.

Ina so in lura nan da nan cewa akwai dabarar buga rami na TCP don kafa haɗin TCP tsakanin nodes a cikin cibiyoyin sadarwa masu zaman kansu daban-daban, amma saboda rashin goyon bayansa ta yawancin na'urorin NAT, yawanci ba a la'akari da shi azaman babbar hanyar haɗi. irin wannan nodes.

Ga ragowar wannan labarin, zan mayar da hankali ne kawai akan aiwatar da yarjejeniyar isar da garantin. Za a bayyana aiwatar da dabarar bugun rami na UDP a cikin labarai masu zuwa.

Abubuwan Bukatun Protocol

  1. Amintaccen isar da fakiti da aka aiwatar ta hanyar ingantacciyar hanyar amsawa (abin da ake kira tabbataccen yarda)
  2. Bukatar ingantaccen canja wurin manyan bayanai, watau. dole ne ka'idar ta guje wa jigilar fakiti mara amfani
  3. Ya kamata a yiyu a soke hanyar tabbatar da isarwa (ikon yin aiki azaman ƙa'idar UDP "tsabta")
  4. Ikon aiwatar da yanayin umarni, tare da tabbatar da kowane saƙo
  5. Mahimmin ɓangaren canja wurin bayanai akan ƙa'idar dole ne ya zama saƙo

Waɗannan buƙatun sun yi daidai da ƙaƙƙarfan ƙa'idodin ƙa'idodin Ka'idar Bayanai da aka bayyana a ciki Farashin 908 и Farashin 1151, kuma na dogara ga waɗannan ƙa'idodin lokacin haɓaka wannan yarjejeniya.

Don fahimtar waɗannan buƙatun, bari mu kalli lokacin canja wurin bayanai tsakanin nodes na cibiyar sadarwa ta amfani da ka'idojin TCP da UDP. Bari a cikin duka biyun za mu sami fakiti ɗaya ya ɓace.
Canja wurin bayanan mara hulɗa akan TCP:Aiwatar da Dogaran Udp yarjejeniya don .Net

Kamar yadda kuke gani daga zane, idan akwai asarar fakiti, TCP zai gano fakitin da ya ɓace kuma ya ba da rahoto ga mai aikawa ta hanyar neman adadin ɓangaren da ya ɓace.
Canja wurin bayanai ta hanyar ka'idar UDP:Aiwatar da Dogaran Udp yarjejeniya don .Net

UDP ba ta ɗaukar matakan gano asara. Gudanar da kurakuran watsawa a cikin ka'idar UDP gaba ɗaya alhakin aikace-aikacen ne.

Ana samun gano kuskure a cikin ƙa'idar TCP ta hanyar kafa haɗin gwiwa tare da kullin ƙarewa, adana yanayin wannan haɗin, nuna adadin bytes da aka aika a cikin kowane fakitin rubutun, da kuma sanar da rasit ta amfani da lambar amincewa.

Bugu da ƙari, don haɓaka aiki (watau aika fiye da kashi ɗaya ba tare da karɓar sanarwa ba), ka'idar TCP tana amfani da abin da ake kira taga watsawa - adadin bytes na bayanan da mai aikawa na sashin ke tsammanin karɓa.

Don ƙarin bayani game da ƙa'idar TCP, duba Farashin 793daga UDP zuwa Farashin 768inda, a gaskiya, an ayyana su.

Daga abin da ke sama, a bayyane yake cewa don ƙirƙirar ƙa'idar isar da saƙo mai dogaro akan UDP (nan gaba ana kiranta da Amintaccen UDP), ana buƙatar aiwatar da hanyoyin canja wurin bayanai kamar TCP. Wato:

  • ajiye yanayin haɗi
  • yi amfani da lambar lamba
  • yi amfani da fakitin tabbatarwa na musamman
  • yi amfani da sauƙaƙan tsarin taga don ƙara yawan kayan aiki na yarjejeniya

Bugu da ƙari, kuna buƙatar:

  • sigina farkon saƙo, don ware albarkatu don haɗin gwiwa
  • sigina ƙarshen saƙo, don isar da saƙon da aka karɓa zuwa aikace-aikacen da ke sama da fitar da albarkatun yarjejeniya
  • ba da damar ƙayyadaddun ƙa'idar haɗin kai don musaki hanyar tabbatar da isar da aiki azaman "tsabta" UDP

Amintaccen shugaban UDP

Ka tuna cewa bayanan UDP yana kunshe a cikin bayanan IP. Fakitin Amintaccen fakitin UDP an “nannade shi” daidai a cikin bayanan UDP.
Dogaran abin rufe kai na UDP:Aiwatar da Dogaran Udp yarjejeniya don .Net

Tsarin abin dogaro na UDP mai sauƙi abu ne mai sauƙi:

Aiwatar da Dogaran Udp yarjejeniya don .Net

  • Tutoci - tutocin sarrafa fakiti
  • MessageType - nau'in saƙon da aikace-aikacen sama ke amfani da su don biyan kuɗi zuwa takamaiman saƙonni
  • TransmissionId - lambar watsawa, tare da adireshi da tashar jiragen ruwa na mai karɓa, musamman gano haɗin.
  • PacketNumber - lambar fakiti
  • Zabuka - ƙarin zaɓuɓɓukan yarjejeniya. A cikin yanayin fakitin farko, ana amfani da shi don nuna girman saƙon

Tutoci sune kamar haka:

  • FirstPacket - fakitin farko na saƙon
  • NoAsk - saƙon baya buƙatar tsarin amincewa don kunna shi
  • LastPacket - fakitin ƙarshe na saƙon
  • RequestForPacket - fakitin tabbatarwa ko buƙatar fakitin da ya ɓace

Gabaɗaya ka'idodin yarjejeniya

Tunda Amintaccen UDP yana mai da hankali kan ingantaccen isar da saƙo tsakanin kuɗaɗe biyu, dole ne ya sami damar kafa haɗi tare da ɗayan ɓangaren. Don kafa haɗin kai, mai aikawa ya aika fakiti tare da tutar FirstPacket, amsawar da za ta nuna haɗin haɗin. Duk fakitin amsa, ko, a wasu kalmomi, fakitin yarda, koyaushe suna saita ƙimar filin PacketNumber zuwa ɗaya fiye da mafi girman ƙimar PacketNumber na fakitin nasarar da aka samu. Filin Zaɓuɓɓuka na fakitin farko da aka aika shine girman saƙon.

Ana amfani da irin wannan hanyar don ƙare haɗin gwiwa. An saita tutar LastPacket akan fakitin ƙarshe na saƙon. A cikin fakitin amsa, an nuna adadin fakitin ƙarshe + 1, wanda ga gefen karɓa yana nufin isar da saƙon nasara.
Tsarin haɗin kai da ƙarewa:Aiwatar da Dogaran Udp yarjejeniya don .Net

Lokacin da aka kafa haɗin, canja wurin bayanai zai fara. Ana watsa bayanai a cikin tubalan fakiti. Kowane toshe, sai na ƙarshe, ya ƙunshi ƙayyadadden adadin fakiti. Yana daidai da girman taga mai karɓa/ watsawa. Tushe na ƙarshe na bayanai na iya samun fakiti kaɗan. Bayan aika kowane toshe, ɓangaren aikawa yana jiran tabbacin isarwa ko buƙatun sake isar da fakitin da suka ɓace, yana barin taga karɓa/ watsawa a buɗe don karɓar amsa. Bayan samun tabbacin isar da toshe, taga mai karɓa/ watsawa yana canzawa kuma ana aika toshe na gaba na bayanai.

Gefen karɓa yana karɓar fakitin. Ana duba kowace fakiti don ganin ko ya faɗi cikin taga watsawa. Fakiti da kwafin da ba su fada cikin taga ana tacewa ba. Domin Idan girman taga ya daidaita kuma daidai yake ga mai karɓa da mai aikawa, to a cikin yanayin bulo na fakiti ana kawowa ba tare da asara ba, ana canza tagar don karɓar fakiti na toshe na gaba kuma tabbatar da isarwa shine. aika. Idan taga bai cika cikin lokacin da mai ƙidayar aiki ya saita ba, to za a fara rajistan abubuwan da ba a ba da fakiti ba kuma za a aika buƙatun sakewa.
Tsarin Sake aikawa:Aiwatar da Dogaran Udp yarjejeniya don .Net

Ƙayyadaddun lokaci da masu ƙidayar yarjejeniya

Akwai dalilai da yawa da yasa ba za a iya kafa haɗi ba. Misali, idan jam'iyyar da ke karba tana layi. A wannan yanayin, lokacin ƙoƙarin kafa haɗi, haɗin za a rufe ta lokacin ƙarewa. Amintaccen aiwatar da UDP yana amfani da masu ƙidayar lokaci biyu don saita lokacin fita. Na farko, mai ƙidayar lokaci, ana amfani da shi don jira amsa daga mai watsa shiri mai nisa. Idan ya yi wuta a gefen mai aikawa, to fakitin da aka aika na ƙarshe ya yi fushi. Idan mai ƙidayar lokaci ya ƙare a wurin mai karɓa, to ana yin rajistan fakitin da aka ɓace kuma ana aika buƙatun sakewa.

Ana buƙatar mai ƙidayar lokaci na biyu don rufe haɗin gwiwa idan akwai rashin sadarwa tsakanin nodes. Ga bangaren mai aikawa, yana farawa nan da nan bayan lokacin aiki ya ƙare, kuma yana jiran amsa daga kullin nesa. Idan babu amsa a cikin ƙayyadadden lokacin, haɗin yana ƙare kuma ana fitar da albarkatun. Don gefen karɓa, ana fara ma'aunin lokacin haɗin haɗin gwiwa bayan mai ƙidayar aiki ya ƙare sau biyu. Wannan ya zama dole don inshora akan asarar fakitin tabbatarwa. Lokacin da mai ƙidayar lokaci ya ƙare, haɗin kuma yana ƙarewa kuma ana fitar da albarkatun.

Dogaro da tsarin watsawa na UDP

Ana aiwatar da ka'idodin ƙa'idar a cikin na'ura mai iyaka, kowace jiha wacce ke da alhakin wasu dabaru na sarrafa fakiti.
Dogaran Jahar UDP:

Aiwatar da Dogaran Udp yarjejeniya don .Net

Rufe - ba da gaske jiha ba ne, wuri ne na farawa da ƙarshen na atomatik. Domin jiha Rufe an karɓi toshe sarrafa watsawa, wanda, aiwatar da uwar garken UDP asynchronous, tura fakiti zuwa hanyoyin haɗin da suka dace kuma ya fara sarrafa jihar.

FirstPacketSending – yanayin farko wanda haɗin ke fita shine lokacin da aka aika saƙon.

A cikin wannan jiha, ana aika fakitin farko don saƙonni na yau da kullun. Don saƙonnin ba tare da tabbatar da aika ba, wannan ita ce kaɗai jihar da ake aika duk saƙon.

Zagayowar Aiki – yanayin ƙasa don watsa fakitin saƙo.

Sauya shi daga jihar FirstPacketSending za'ayi bayan an aika fakitin farko na saƙon. A cikin wannan yanayin ne duk yabo da buƙatun sake aikawa suka zo. Fita daga gare ta yana yiwuwa a lokuta biyu - idan an sami nasarar isar da saƙon ko ta lokacin ƙarewa.

FirstPacket An Karba – yanayin farko ga mai karɓar saƙon.

Yana bincika daidaiton farkon watsawa, ƙirƙirar tsarin da ake buƙata, kuma yana aika takardar shaidar karɓar fakitin farko.

Don saƙon da ya ƙunshi fakiti guda ɗaya kuma an aika ba tare da amfani da shaidar isarwa ba, wannan ita kaɗai ce jiha. Bayan sarrafa irin wannan saƙon, haɗin yana rufe.

Haɗuwa – ainihin yanayin karɓar fakitin saƙo.

Yana rubuta fakiti zuwa ajiya na wucin gadi, bincika asarar fakiti, aika da godiya don isar da buƙatun fakiti da dukkan saƙon, kuma yana aika buƙatun sakewa na fakitin da suka ɓace. Idan an sami nasarar karɓar duk saƙon, haɗin yana shiga cikin jihar kammala, in ba haka ba, lokaci ya ƙare.

kammala - rufe haɗin gwiwa idan an sami nasarar karɓar duk saƙon.

Wannan yanayin ya zama dole don taron saƙon da kuma shari'ar lokacin da isar da tabbacin saƙon ya ɓace akan hanyar zuwa ga mai aikawa. Ana fitar da wannan jihar ta hanyar ƙarewar lokaci, amma ana ɗaukar haɗin cikin nasara a rufe.

Zurfafa cikin code. naúrar sarrafa watsawa

Ɗaya daga cikin mahimman abubuwan Amintaccen UDP shine toshe sarrafa watsawa. Ayyukan wannan toshe shine adana abubuwan haɗin kai na yanzu da abubuwan taimako, rarraba fakiti masu shigowa zuwa haɗin haɗin da suka dace, samar da hanyar sadarwa don aika fakiti zuwa haɗin gwiwa, da aiwatar da API yarjejeniya. Tushen sarrafa watsawa yana karɓar fakiti daga Layer UDP kuma yana tura su zuwa injin jihar don sarrafawa. Don karɓar fakiti, yana aiwatar da sabar UDP asynchronous.
Wasu membobin ajin 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;    	
  //...
}

Aiwatar da sabar 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);
}

Ga kowane canja wurin saƙo, an ƙirƙiri tsari wanda ya ƙunshi bayanai game da haɗin gwiwa. Ana kiran irin wannan tsari rikodin haɗin gwiwa.
Wasu membobin ajin 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;
  //...
}

Zurfafa cikin code. jihohi

Jihohi suna aiwatar da injin Jiha na ka'idar UDP mai dogaro, inda babban sarrafa fakiti ke gudana. ReliableUdpState na abstract yana ba da hanyar sadarwa ga jihar:

Aiwatar da Dogaran Udp yarjejeniya don .Net

Dukkanin dabaru na yarjejeniya ana aiwatar da su ta hanyar azuzuwan da aka gabatar a sama, tare da ajin taimako wanda ke ba da hanyoyi masu tsayi, kamar, misali, gina taken ReliableUdp daga rikodin haɗin.

Na gaba, za mu yi la'akari dalla dalla dalla dalla dalla dalla dalla dalla-dalla na aiwatar da hanyoyin sadarwa waɗanda ke ƙayyade ainihin algorithms na yarjejeniya.

Hanyar DisposeByTimeout

Hanyar DisposeByTimeout ita ce ke da alhakin fitar da albarkatun haɗin kai bayan an ƙare lokaci da kuma siginar isar da saƙo mai nasara/ rashin nasara.
AmintacceUdpState.BayyanaByTimeout:

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

Abin kawai ya wuce gona da iri a cikin jihar kammala.
An Kammala. Zubar da Lokaci:

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

HanyarPackets

HanyarPackets tana da alhakin ƙarin sarrafa fakiti ko fakiti. Ana kiransa kai tsaye ko ta hanyar mai ƙidayar fakiti.

Iya Haɗuwa Hanyar ta wuce gona da iri kuma tana da alhakin bincika fakitin da suka ɓace da canzawa zuwa jihar kammala, idan har an sami fakitin ƙarshe da kuma wucewa cikin nasara cak
Tattara.Tsarin Fakiti:

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

Iya Zagayowar Aiki Ana kiran wannan hanyar akan mai ƙidayar lokaci kawai, kuma tana da alhakin sake aika saƙon ƙarshe, tare da ba da damar haɗa lokacin kusa.
AikaCycle.Packets Process:

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

Iya kammala hanyar tana dakatar da mai ƙidayar lokaci kuma ta aika saƙo ga masu biyan kuɗi.
An Kammala.Packets:

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

Hanyar karɓarPacket

Iya FirstPacket An Karba Babban aikin hanyar shine tantance ko ainihin fakitin saƙon ya isa wurin sadarwa, da kuma tattara saƙon da ya ƙunshi fakiti ɗaya.
Fakitin Farko An Karɓi.Takardar Karɓa:

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

Iya Zagayowar Aiki Wannan hanyar an yi watsi da ita don karɓar amincewar isarwa da buƙatun sake aikawa.
AikaCycle.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));
}

Iya Haɗuwa a cikin hanyar ReceivePacket, babban aikin haɗa saƙo daga fakiti masu shigowa yana gudana.
Tattaunawa.Packet:

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

Iya kammala kawai aikin hanyar shine a aika da sake amincewa da nasarar isar da saƙon.
An Kammala.Takarda Karɓa:

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

Hanyar Aika Fakiti

Iya FirstPacketSending wannan hanyar tana aika fakitin bayanai na farko, ko, idan saƙon baya buƙatar tabbatar da isarwa, duk saƙon.
PacketSending.AikaPacket:

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

Iya Zagayowar Aiki a cikin wannan hanya, an aika da toshe fakiti.
AikaCycle.Packet Aika:

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

Zurfafa cikin code. Ƙirƙirar da Ƙirƙirar Haɗuwa

Yanzu da muka ga jahohi na asali da kuma hanyoyin da ake amfani da su wajen tafiyar da jihohi, bari mu taƙaita ‘yan misalai na yadda ka’idar ke aiki dalla-dalla.
Jadawalin watsa bayanai a ƙarƙashin yanayin al'ada:Aiwatar da Dogaran Udp yarjejeniya don .Net

Yi la'akari daki-daki da halittar rikodin haɗin gwiwa don haɗawa da aika fakitin farko. Ana ƙaddamar da canja wuri koyaushe ta aikace-aikacen da ke kiran saƙon API. Bayan haka, ana kiran hanyar StartTransmission na toshe sarrafa watsawa, wanda ke fara watsa bayanai don sabon saƙo.
Ƙirƙirar haɗi mai fita:

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

Aika fakitin farko (Jihar FarkoPacketSending):

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

Bayan aika fakitin farko, mai aikawa ya shiga jihar Zagayowar Aiki – jira tabbatar da isar da kunshin.
Bangaren karɓa, ta amfani da hanyar Ƙarshen Receive, yana karɓar fakitin da aka aika, yana ƙirƙirar sabo rikodin haɗin gwiwa kuma ya wuce wannan fakiti, tare da rubutun da aka riga aka yi, zuwa hanyar ReceivePacket na jihar don sarrafawa. FirstPacket An Karba
Ƙirƙirar haɗi a gefen karɓa:

private void EndReceive(IAsyncResult ar)
{
  // ...
  // пакет получен
  // парсим заголовок пакета        
  ReliableUdpHeader header;
  if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header))
  {          
    // пришел некорректный пакет - отбрасываем его
    return;
  }
  // конструируем ключ для определения connection record’а для пакета
  Tuple<EndPoint, Int32> key = new Tuple<EndPoint, Int32>(connectedClient, header.TransmissionId);
  // получаем существующую connection record или создаем новую
  ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header. ReliableUdpMessageType));
  // запускаем пакет в обработку в конечный автомат
  record.State.ReceivePacket(record, header, bytes);
}

Karɓar fakitin farko da aika sanarwa (jihar da aka karɓa ta farko):

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

Zurfafa cikin code. Rufe haɗin kan lokacin ƙarewa

Gudanar da lokacin ƙayyadaddun lokaci muhimmin bangare ne na Dogaran UDP. Yi la'akari da misali a cikin abin da tsaka-tsakin kumburi ya gaza kuma isar da bayanai a bangarorin biyu ya zama ba zai yiwu ba.
Zane don rufe haɗin gwiwa ta lokacin ƙarewa:Aiwatar da Dogaran Udp yarjejeniya don .Net

Kamar yadda ake iya gani daga zane, lokacin aikin mai aikawa yana farawa nan da nan bayan ya aika da toshe fakiti. Wannan yana faruwa a cikin hanyar SendPacket na jihar Zagayowar Aiki.
Ƙaddamar da lokacin aikin (SendingCycle state):

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

Ana saita lokutan ƙidayar lokacin da aka ƙirƙiri haɗin. Tsoffin ShortTimerPeriod shine daƙiƙa 5. A cikin misali, an saita shi zuwa 1,5 seconds.

Don haɗi mai shigowa, mai ƙidayar lokaci yana farawa bayan karɓar fakitin bayanan mai shigowa na ƙarshe, wannan yana faruwa a cikin hanyar ReceivePacket na jihar. Haɗuwa
Ƙaddamar da lokacin aiki (Haɗin Jiha):

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

Babu ƙarin fakiti da suka isa kan haɗin mai shigowa yayin jiran mai ƙidayar aiki. Mai ƙidayar lokaci ya tafi ya kira hanyar ProcessPackets, inda aka samo fakitin da suka ɓace kuma an aika buƙatun sakewa a karon farko.
Aika buƙatun sakewa (Haɗin Jiha):

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

An saita canjin TimeSecondTry zuwa gaskiya. Wannan canjin yana da alhakin sake kunna lokacin aiki.

A gefen mai aikawa, mai ƙidayar lokacin aiki shima yana kunna kuma fakitin da aka aiko na ƙarshe yana jin daɗi.
Ƙaddamar da mai ƙidayar lokaci mai haɗi (SendingCycle state):

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

Bayan haka, lokacin haɗin haɗin gwiwa yana farawa a cikin haɗin mai fita.
AmintaccenUdpState.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);
}

Tsawon lokacin ƙarewar lokacin haɗin haɗin yana da daƙiƙa 30 ta tsohuwa.

Bayan ɗan gajeren lokaci, mai ƙidayar aiki a gefen mai karɓa ya sake yin wuta, ana sake aika buƙatun, bayan haka mai ƙididdige ƙididdigewa yana farawa don haɗin mai shigowa.

Lokacin da masu ƙidayar lokaci suka yi wuta, ana fitar da duk albarkatun bayanan haɗin biyu. Mai aikawa ya ba da rahoton gazawar isarwa ga aikace-aikacen sama (duba Amintaccen UDP API).
Saki albarkatun rikodin haɗin kai:

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

Zurfafa cikin code. Ana dawo da canja wurin bayanai

Jadawalin dawo da watsa bayanai idan aka yi asarar fakiti:Aiwatar da Dogaran Udp yarjejeniya don .Net

Kamar yadda aka riga aka tattauna a cikin rufe haɗin kan lokacin ƙarewa, lokacin da lokacin aiki ya ƙare, mai karɓa zai duba fakitin da suka ɓace. Idan aka yi asarar fakiti, za a haɗa jerin adadin fakitin da ba su kai ga mai karɓa ba. Ana shigar da waɗannan lambobi a cikin tsarin LostPackets na takamaiman haɗin gwiwa, kuma ana aika buƙatun sakewa.
Aika buƙatun don maido da fakiti (Haɗin Jiha):

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

Mai aikawa zai karɓi buƙatun sakewa kuma ya aika fakitin da suka ɓace. Yana da mahimmanci a lura cewa a wannan lokacin mai aikawa ya riga ya fara haɗin haɗin lokaci kusa kuma, lokacin da aka karɓi buƙatun, an sake saita shi.
Ana sake aika fakitin da suka ɓace (Jihar Aika Cycle):

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

Fakitin resent (fakiti #3 a cikin zane) yana karɓar haɗin mai shigowa. Ana bincika don ganin idan taga mai karɓa ya cika kuma an dawo da watsa bayanai na yau da kullun.
Ana duba hits a cikin taga mai karɓa (Haɗin Jiha):

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

Amintaccen UDP API

Don yin hulɗa tare da ƙa'idar canja wurin bayanai, akwai buɗaɗɗen ajin Amintaccen Udp, wanda shine abin rufewa akan toshe sarrafa canja wuri. Ga mafi mahimmancin membobin ajin:

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

Ana karɓar saƙonni ta hanyar biyan kuɗi. Wakilci sa hannun don hanyar dawo da kira:

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

Sakamakon:

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

Don biyan kuɗi zuwa takamaiman nau'in saƙo da/ko takamaiman mai aikawa, ana amfani da sigogi na zaɓi biyu: ReliableUdpMessageTypes messageType da IPEndPoint ipEndPoint.

Nau'in saƙo:

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

Ana aika saƙon ba tare da ɓata lokaci ba; saboda wannan, ƙa'idar tana aiwatar da ƙirar shirye-shiryen asynchronous:

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

Sakamakon aika saƙon zai zama gaskiya - idan sakon ya sami nasarar isa ga mai karɓa kuma karya - idan an rufe haɗin ta hanyar ƙarewa:

public bool EndSendMessage(IAsyncResult asyncResult)

ƙarshe

Ba a bayyana da yawa a cikin wannan labarin ba. Hanyoyin daidaita zaren, keɓancewa da sarrafa kuskure, aiwatar da hanyoyin aika saƙon da ba daidai ba. Amma ainihin ƙa'idar, bayanin ma'anar sarrafa fakiti, kafa haɗin gwiwa, da sarrafa lokacin ƙarewa, ya kamata ya bayyana a gare ku.

Sigar ingantaccen tsarin isarwa yana da ƙarfi da sassauƙa don biyan buƙatun da aka ayyana a baya. Amma ina so in ƙara cewa za a iya inganta aiwatar da aikin da aka bayyana. Alal misali, don ƙara yawan kayan aiki da kuma canza canjin lokaci na lokaci, hanyoyin kamar taga mai zamewa da RTT za a iya ƙarawa a cikin yarjejeniya, kuma zai zama da amfani don aiwatar da tsarin ƙayyade MTU tsakanin nodes na haɗi (amma idan an aika manyan saƙonni). .

Na gode da kulawar ku, ina jiran sharhi da sharhi.

PS Ga waɗanda ke da sha'awar cikakkun bayanai ko kawai suna son gwada ƙa'idar, hanyar haɗi zuwa aikin akan GitHube:
Amintaccen aikin UDP

Hanyoyi masu amfani da labarai

  1. Bayanin ƙayyadaddun ƙa'idodin TCP: a Turanci и in Rashanci
  2. Ƙayyadaddun ƙa'idodin UDP: a Turanci и in Rashanci
  3. Tattaunawar ka'idar RUDP: daftarin-ietf-sigtran-amintaccen-udp-00
  4. Dogaran Data Protocol: Farashin 908 и Farashin 1151
  5. Sauƙaƙan aiwatar da tabbatar da isarwa akan UDP: Ɗauki Gabaɗaya Sarrafa Sadarwar Sadarwar ku Tare da NET Da UDP
  6. Labarin da ke bayyana hanyoyin wucewa ta NAT: Sadarwar Tsari-da-Kwarai A Fassarar Adireshin Sadarwar Sadarwa
  7. Aiwatar da samfurin shirye-shiryen asynchronous: Aiwatar da CLR Asynchronous Programming Model и Yadda ake aiwatar da tsarin ƙirar IAsyncResult
  8. Ƙaddamar da samfurin shirye-shiryen asynchronous zuwa tsarin asynchronous na tushen ɗawainiya (APM a cikin TAP):
    TPL da Gargajiya .NET Asynchronous Programming
    Yin hulɗa tare da Wasu Samfuran Asynchronous da Nau'o'in

Sabuntawa: Na gode magajin gari и sidristij don ra'ayin ƙara aiki zuwa dubawa. Ba a keta daidaituwar ɗakin karatu tare da tsoffin tsarin aiki ba, saboda Tsarin 4th yana goyan bayan duka XP da uwar garken 2003.

source: www.habr.com

Add a comment