Palaksanaan protokol Udp dipercaya pikeun .Net

Internét geus lila robah. Salah sahiji protokol utama Internét - UDP dianggo ku aplikasi henteu ngan ukur pikeun nganteurkeun datagram sareng siaran, tapi ogé pikeun nyayogikeun sambungan "peer-to-peer" antara titik jaringan. Kusabab desain anu sederhana, protokol ieu ngagaduhan seueur kagunaan anu teu direncanakeun sateuacana, tapi kakurangan protokol, sapertos kurangna pangiriman anu dijamin, henteu leungit di mana waé. Artikel ieu ngajelaskeun palaksanaan protokol pangiriman dijamin leuwih UDP.
eusi:asup
Syarat Protokol
lulugu UDP dipercaya
Prinsip umum tina protokol
Timeouts na timers protokol
Diagram kaayaan transmisi UDP dipercaya
Deeper kana kode. Unit kontrol transmisi
Deeper kana kode. nagara bagian

Deeper kana kode. Nyiptakeun sareng Ngadegkeun Sambungan
Deeper kana kode. Nutup sambungan on timeout
Deeper kana kode. Malikkeun mindahkeun data
UDP API dipercaya
kacindekan
Tumbu mangpaat jeung artikel

asup

Arsitéktur asli Internét nganggap rohangan alamat homogen dimana unggal titik gaduh alamat IP global sareng unik sareng tiasa komunikasi langsung sareng titik sanés. Ayeuna Internét, kanyataanna, gaduh arsitéktur anu béda - hiji daérah alamat IP global sareng seueur daérah anu alamat pribadi disumputkeun tukangeun alat NAT.Dina arsitéktur ieu, ngan ukur alat dina rohangan alamat global anu tiasa gampang komunikasi sareng saha waé dina jaringan sabab gaduh alamat IP anu unik sareng tiasa dirobih sacara global. Titik dina jaringan pribadi tiasa nyambung ka titik sanés dina jaringan anu sami, sareng ogé tiasa nyambung ka titik-titik sanés anu terkenal dina rohangan alamat global. Interaksi ieu kahontal utamana alatan mékanisme tarjamahan alamat jaringan. Alat NAT, sapertos router Wi-Fi, ngadamel éntri tabel tarjamahan khusus pikeun sambungan kaluar sareng ngarobih alamat IP sareng nomer port dina pakét. Hal ieu ngamungkinkeun sambungan kaluar ti jaringan pribadi ka host dina spasi alamat global. Tapi dina waktos anu sami, alat NAT biasana meungpeuk sadaya lalu lintas anu asup kecuali aturan anu misah pikeun sambungan asup diatur.

Arsitéktur Internét ieu cukup leres pikeun komunikasi klien-server, dimana klien tiasa aya dina jaringan pribadi, sareng server gaduh alamat global. Tapi nyiptakeun kasusah pikeun sambungan langsung dua titik antara rupa-rupa jaringan pribadi. Sambungan langsung antara dua titik penting pikeun aplikasi peer-to-peer kayaning transmisi sora (Skype), meunangkeun aksés jauh ka komputer (TeamViewer), atawa kaulinan online.

Salah sahiji metodeu anu paling mujarab pikeun ngadamel sambungan peer-to-peer antara alat dina jaringan pribadi anu béda disebut punching liang. Téhnik ieu paling sering dianggo sareng aplikasi dumasar kana protokol UDP.

Tapi upami aplikasi anjeun peryogi pangiriman data anu dijamin, contona, anjeun nransferkeun file antara komputer, maka ngagunakeun UDP bakal ngalaman seueur kasusah kusabab kanyataan yén UDP sanés protokol pangiriman anu dijamin sareng henteu nyayogikeun pangiriman pakét dina urutan, teu sapertos TCP. protokol.

Dina hal ieu, pikeun mastikeun pangiriman pakét dijamin, éta diperlukeun pikeun nerapkeun hiji protokol lapisan aplikasi nu nyadiakeun fungsionalitas diperlukeun tur dianggo leuwih UDP.

Abdi hoyong langsung dicatet yén aya téhnik punching liang TCP pikeun ngadegkeun sambungan TCP antara titik dina jaringan pribadi béda, tapi alatan kurangna rojongan pikeun eta ku loba alat NAT, biasana teu dianggap salaku jalan utama pikeun nyambungkeun. titik sapertos.

Pikeun sésana tina artikel ieu, kuring bakal museurkeun ukur dina palaksanaan protokol pangiriman dijamin. Palaksanaan téhnik punching liang UDP bakal dijelaskeun dina artikel di handap ieu.

Syarat Protokol

  1. Pangiriman pakét anu dipercaya dilaksanakeun ngaliwatan mékanisme eupan balik positif (nu disebut pangakuan positif)
  2. Kabutuhan pikeun mindahkeun efisien data badag, i.e. protokol kudu nyingkahan relaying pakét perlu
  3. Kedah tiasa ngabatalkeun mékanisme konfirmasi pangiriman (kamampuhan pikeun fungsina salaku protokol UDP "murni")
  4. Kamampuhan pikeun nerapkeun mode paréntah, kalayan konfirmasi unggal pesen
  5. Unit dasar transfer data ngaliwatan protokol kudu pesen

Sarat ieu sabagéan ageung saluyu sareng sarat Protokol Data Reliable anu dijelaskeun dina RFC 908 и RFC 1151, sareng kuring ngandelkeun standar éta nalika ngembangkeun protokol ieu.

Pikeun ngartos sarat ieu, hayu urang tingali waktos transfer data antara dua titik jaringan nganggo protokol TCP sareng UDP. Hayu dina dua kasus urang bakal leungit hiji pakét.
Mindahkeun data non-interaktif ngaliwatan TCP:Palaksanaan protokol Udp dipercaya pikeun .Net

Sakumaha anjeun tiasa tingali tina diagram, upami pakét leungitna, TCP bakal ngadeteksi pakét anu leungit sareng ngalaporkeun ka pangirim ku naroskeun nomer bagian anu leungit.
Mindahkeun data ngaliwatan protokol UDP:Palaksanaan protokol Udp dipercaya pikeun .Net

UDP henteu nyandak léngkah deteksi leungitna. Kontrol kasalahan pangiriman dina protokol UDP sagemblengna tanggung jawab aplikasi.

Deteksi kasalahan dina protokol TCP dihontal ku cara nyieun sambungan sareng titik tungtung, nyimpen kaayaan sambungan éta, nunjukkeun jumlah bait anu dikirim dina unggal header pakét, sareng ngabéjaan kuitansi nganggo nomer pangakuan.

Salaku tambahan, pikeun ningkatkeun kinerja (nyaéta ngirim langkung ti hiji bagéan tanpa nampi pangakuan), protokol TCP nganggo anu disebut jandela transmisi - jumlah bait data anu diperkirakeun nampi pangirim segmen.

Kanggo inpo nu langkung lengkep ihwal protokol TCP, tingali RFC 793, ti UDP ka RFC 768dimana, kanyataanna, aranjeunna dihartikeun.

Tina di luhur, jelas yén pikeun nyiptakeun protokol pangiriman pesen anu dipercaya dina UDP (saterusna disebut UDP anu dipercaya), diperlukeun pikeun nerapkeun mékanisme mindahkeun data sarupa TCP. Nyaéta:

  • nyimpen kaayaan sambungan
  • ngagunakeun panomeran ruas
  • ngagunakeun bungkusan konfirmasi husus
  • ngagunakeun mékanisme windowing disederhanakeun pikeun ngaronjatkeun throughput protokol

Salaku tambahan, anjeun peryogi:

  • sinyal mimiti pesen, pikeun allocate sumberdaya pikeun sambungan nu
  • Sinyal tungtung pesen, pikeun ngirim pesen anu ditampi ka aplikasi hulu sareng ngabebaskeun sumber protokol
  • ngidinan protokol sambungan-spésifik nganonaktipkeun mékanisme konfirmasi pangiriman pikeun fungsi salaku "murni" UDP

lulugu UDP dipercaya

Émut yén datagram UDP dibungkus dina datagram IP. Paket UDP anu Dipercanten leres-leres "dibungkus" kana datagram UDP.
Enkapsulasi lulugu UDP anu dipercaya:Palaksanaan protokol Udp dipercaya pikeun .Net

Struktur lulugu UDP Reliable cukup basajan:

Palaksanaan protokol Udp dipercaya pikeun .Net

  • Bandéra - pakét kontrol bandéra
  • MessageType - jinis pesen anu dianggo ku aplikasi hulu pikeun ngalanggan pesen khusus
  • TransmissionId - jumlah pangiriman, sareng alamat sareng palabuhan panarima, sacara unik ngidentipikasi sambunganna
  • PacketNumber - nomer pakét
  • Pilihan - pilihan protokol tambahan. Dina kasus pakét munggaran, éta dianggo pikeun nunjukkeun ukuran pesen

Bandéra nyaéta kieu:

  • FirstPacket - pakét mimiti pesen
  • NoAsk - pesen henteu meryogikeun mékanisme pangakuan pikeun diaktipkeun
  • LastPacket - pakét panungtungan pesen
  • RequestForPacket - pakét konfirmasi atanapi nyuhunkeun pakét anu leungit

Prinsip umum tina protokol

Kusabab UDP Reliable museurkeun kana pangiriman pesen anu dijamin antara dua titik, éta kedah tiasa ngadamel sambungan sareng sisi anu sanés. Pikeun nyieun konéksi, pangirim ngirim pakét sareng bandéra FirstPacket, réspon anu hartosna sambunganna didamel. Kabéh pakét respon, atawa, dina basa sejen, pakét pangakuan, salawasna nyetel nilai widang PacketNumber ka hiji leuwih ti nilai PacketNumber pangbadagna pakét hasil narima. Widang Pilihan pikeun pakét munggaran anu dikirim nyaéta ukuran pesen.

Mékanisme anu sami dianggo pikeun ngeureunkeun sambungan. Bandéra LastPacket disetel dina pakét terakhir suratna. Dina pakét respon, jumlah pakét panungtungan + 1 dituduhkeun, nu keur sisi panarima hartina pangiriman suksés pesen.
Diagram sambungan sareng terminasi:Palaksanaan protokol Udp dipercaya pikeun .Net

Nalika sambungan didamel, transfer data dimimitian. Data dikirimkeun dina blok pakét. Unggal blok, iwal hiji panungtungan, ngandung jumlah tetep pakét. Éta sami sareng ukuran jandela nampi / ngirimkeun. Blok data anu terakhir tiasa gaduh pakét langkung sakedik. Saatos ngirim unggal blok, sisi ngirim ngantosan konfirmasi pangiriman atanapi pamenta pikeun ngirimkeun deui pakét anu leungit, ngantepkeun jandela nampi / ngirimkeun kabuka pikeun nampi réspon. Saatos nampi konfirmasi pangiriman blok, jandela nampi / ngirimkeun ngageser sareng blok data salajengna dikirim.

Sisi panarima narima pakét. Unggal pakét dipariksa pikeun ningali naha éta aya dina jandela pangiriman. Pakét sareng duplikat anu henteu digolongkeun kana jandela disaring. Sabab Upami ukuran jandela tetep sareng sami pikeun panarima sareng pangirim, maka dina kasus blok pakét anu dikirimkeun tanpa kaleungitan, jandela digeser pikeun nampi pakét blok data salajengna sareng konfirmasi pangiriman nyaéta dikirim. Upami jandelana henteu ngeusian dina jangka waktu anu disetél ku pangatur waktu kerja, cék bakal ngamimitian dimana pakét henteu acan dikirimkeun sareng pamundut pikeun pangiriman deui bakal dikirim.
Diagram pangiriman deui:Palaksanaan protokol Udp dipercaya pikeun .Net

Timeouts na timers protokol

Aya sababaraha alesan naha sambungan teu bisa ngadegkeun. Contona, upami pihak narima offline. Dina hal ieu, nalika nyobian nyieun sambungan, sambungan bakal ditutup ku timeout. Palaksanaan UDP Reliable ngagunakeun dua timers pikeun nyetel timeouts. Kahiji, timer gawe, dipaké pikeun ngadagoan respon ti host jauh. Upami seuneu di sisi pangirim, teras pakét anu terakhir dikirimkeun deui. Lamun timer kadaluwarsa di panarima, teras dipariksa pikeun pakét leungit dipigawé sarta requests pikeun redelivery dikirim.

Timer kadua diperlukeun pikeun nutup sambungan bisi kurangna komunikasi antara titik. Pikeun sisi pangirim, éta dimimitian langsung saatos waktos damel, sareng ngantosan réspon ti titik jauh. Upami teu aya réspon dina waktos anu ditangtukeun, sambunganna diputus sareng sumber daya dileupaskeun. Pikeun sisi panarima, pangatur waktu nutup sambungan dimimitian sanggeus timer gawé kadaluwarsa dua kali. Ieu diperlukeun pikeun nanggungkeun ka kantor asuransi ngalawan leungitna pakét konfirmasi. Nalika timer kadaluwarsa, sambungan ogé terminated tur sumberdaya dileupaskeun.

Diagram kaayaan transmisi UDP dipercaya

Prinsip protokol anu dilaksanakeun dina mesin kaayaan terhingga, unggal kaayaan nu tanggung jawab logika tangtu processing pakét.
Diagram kaayaan UDP anu dipercaya:

Palaksanaan protokol Udp dipercaya pikeun .Net

katutup - teu bener kaayaan, eta mangrupakeun titik mimiti jeung tungtung pikeun otomat. Pikeun kaayaan katutup blok kontrol transmisi narima, nu, ngalaksanakeun hiji server UDP Asynchronous, diteruskeun pakét ka sambungan luyu tur mimitian processing kaayaan.

FirstPacketSending - kaayaan awal dimana sambungan kaluar nalika pesen dikirim.

Dina kaayaan ieu, pakét munggaran pikeun pesen normal dikirim. Pikeun pesen tanpa konfirmasi kirim, ieu mangrupikeun hiji-hijina kaayaan dimana sadaya pesen dikirim.

Siklus Ngirim - kaayaan taneuh pikeun pangiriman pakét pesen.

Transisi ka dinya ti nagara FirstPacketSending Dilaksanakeun saatos pakét munggaran pesen parantos dikirim. Dina kaayaan ieu, sadaya pangakuan sareng pamenta pikeun pangiriman ulang sumping. Kaluar tina éta mungkin dina dua kasus - dina kasus pangiriman pesen anu suksés atanapi ku waktos béak.

FirstPacketReceived – kaayaan awal pikeun panarima suratna.

Éta mariksa kabeneran awal pangiriman, nyiptakeun struktur anu diperyogikeun, sareng ngirimkeun pangakuan nampi pakét munggaran.

Pikeun pesen anu diwangun ku pakét tunggal sareng dikirim tanpa nganggo bukti pangiriman, ieu mangrupikeun hiji-hijina kaayaan. Saatos ngolah pesen sapertos kitu, sambunganna ditutup.

Assembling - kaayaan dasar pikeun nampi pakét pesen.

Éta nyerat pakét pikeun neundeun samentawis, mariksa pakét leungitna, ngirim pangakuan pikeun pangiriman blok pakét sareng sadaya pesen, sareng ngirim pamenta pikeun pangiriman deui pakét anu leungit. Dina hal resi suksés sakabéh pesen, sambungan nu mana kana kaayaan réngsé, Upami teu kitu, waktos kaluar kaluar.

réngsé – nutup sambungan bisi sukses narima sakabéh pesen.

Kaayaan ieu dipikabutuh pikeun majelis pesen sareng pikeun kasus nalika konfirmasi pangiriman suratna leungit dina jalan ka pangirim. Kaayaan ieu kaluar ku waktos béak, tapi sambunganna dianggap suksés ditutup.

Deeper kana kode. Unit kontrol transmisi

Salah sahiji elemen konci UDP Reliable nyaéta blok kontrol transmisi. Tugas blok ieu pikeun nyimpen sambungan ayeuna jeung elemen bantu, ngadistribusikaeun pakét asup ka sambungan pakait, nyadiakeun antarbeungeut pikeun ngirim pakét ka sambungan, sarta nerapkeun API protokol. Blok kontrol transmisi nampi pakét ti lapisan UDP sareng diteruskeun kana mesin kaayaan pikeun diolah. Pikeun nampi pakét, éta ngalaksanakeun server UDP Asynchronous.
Sababaraha anggota kelas 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;    	
  //...
}

Palaksanaan server UDP asinkron:

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

Pikeun unggal mindahkeun pesen, struktur dijieun nu ngandung émbaran ngeunaan sambungan nu. Struktur sapertos kitu disebut rékaman sambungan.
Sababaraha anggota kelas 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;
  //...
}

Deeper kana kode. nagara bagian

Nagara nerapkeun mesin kaayaan tina protokol UDP Reliable, dimana processing utama pakét lumangsung. Kelas abstrak ReliableUdpState nyadiakeun antarbeungeut pikeun kaayaan:

Palaksanaan protokol Udp dipercaya pikeun .Net

Sakabeh logika protokol dilaksanakeun ku kelas-kelas anu disayogikeun di luhur, sareng kelas bantu anu nyayogikeun metode statik, sapertos, contona, ngawangun header ReliableUdp tina catetan sambungan.

Salajengna, urang bakal mertimbangkeun sacara rinci palaksanaan metode antarmuka anu nangtukeun algoritma dasar protokol.

Métode DisposeByTimeout

Métode DisposeByTimeout tanggung jawab pikeun ngaleupaskeun sumber sambungan saatos waktosna sareng masihan sinyal pangiriman pesen anu suksés/gagal.
ReliableUdpState.DisposeByTimeout:

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

Ieu ngan overridden di nagara bagian réngsé.
Réngsé.DisposeByTimeout:

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

Métode ProcessPackets

Metodeu ProcessPackets tanggung jawab pikeun ngolah tambahan tina pakét atanapi bungkusan. Ditelepon langsung atanapi liwat pakét ngantosan timer.

Sanggup Assembling Metoda ieu overridden sarta tanggung jawab pikeun pariksa pakét leungit tur transisi ka nagara réngsé, bisi narima pakét panungtungan sarta lulus cek suksés
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);
  }
}

Sanggup Siklus Ngirim Metoda ieu disebut ukur dina timer a, sarta tanggung jawab pikeun ngirim deui pesen panungtungan, kitu ogé sangkan sambungan timer nutup.
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);
}

Sanggup réngsé métode eureun timer ngajalankeun sarta ngirimkeun pesen ka palanggan.
Réngsé.ProsésPakét:

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

Métode ReceivePacket

Sanggup FirstPacketReceived tugas utama metoda nyaeta pikeun nangtukeun naha pakét pesen munggaran sabenerna anjog di panganteur, sarta ogé pikeun ngumpulkeun pesen diwangun ku hiji pakét tunggal.
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);
  }
}

Sanggup Siklus Ngirim métode ieu overridden pikeun nampa acknowledgments pangiriman jeung requests retransmission.
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));
}

Sanggup Assembling dina métode ReceivePacket, karya utama assembling pesen ti pakét asup lumangsung.
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);
  }
}

Sanggup réngsé hijina tugas metoda nyaeta ngirim hiji ulang pangakuan ti pangiriman suksés pesen.
Rengse.ReceivePacket:

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

Kirim Pakét Métode

Sanggup FirstPacketSending Metoda ieu ngirim pakét data munggaran, atawa lamun talatah teu merlukeun konfirmasi pangiriman, sakabéh pesen.
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);
}

Sanggup Siklus Ngirim dina metoda ieu, hiji blok pakét dikirim.
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 );
  }
}

Deeper kana kode. Nyiptakeun sareng Ngadegkeun Sambungan

Ayeuna urang parantos ningali kaayaan dasar sareng metode anu dianggo pikeun nanganan nagara, hayu urang ngarecah sababaraha conto kumaha protokol jalanna langkung rinci.
Diagram pangiriman data dina kaayaan normal:Palaksanaan protokol Udp dipercaya pikeun .Net

Mertimbangkeun di jéntré kreasi rékaman sambungan pikeun nyambungkeun jeung ngirim pakét munggaran. Transperna sok diprakarsai ku aplikasi anu nyauran API ngirim pesen. Salajengna, métode StartTransmission tina blok kontrol transmisi invoked, nu dimimitian pangiriman data pikeun pesen anyar.
Nyieun sambungan kaluar:

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

Ngirim pakét munggaran (kaayaan FirstPacketSending):

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

Saatos ngirim pakét munggaran, pangirim asup ka nagara Siklus Ngirim – antosan konfirmasi pangiriman pakét.
Sisi panarima, ngagunakeun métode EndReceive, narima pakét dikirim, nyieun anyar rékaman sambungan sareng ngalangkungan pakét ieu, kalayan lulugu anu tos diparsasi, kana metode ReceivePacket nagara pikeun ngolah FirstPacketReceived
Nyieun sambungan di sisi panarima:

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

Nampa pakét munggaran sareng ngirim pangakuan (kaayaan FirstPacketReceived):

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

Deeper kana kode. Nutup sambungan on timeout

Penanganan timeout mangrupa bagian penting tina UDP Reliable. Pertimbangkeun conto dimana titik panengah gagal sareng pangiriman data dina dua arah janten teu mungkin.
Diagram pikeun nutup sambungan ku waktu béak:Palaksanaan protokol Udp dipercaya pikeun .Net

Sapertos tiasa ditingali tina diagram, pangatur waktu pangirim dimimitian langsung saatos ngirim blok pakét. Ieu lumangsung dina metoda SendPacket nagara Siklus Ngirim.
Aktipkeun timer gawe (kaayaan SendingCycle):

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

Periode timer disetel nalika sambungan dijieun. ShortTimerPeriod standar nyaéta 5 detik. Dina conto, disetel ka 1,5 detik.

Pikeun sambungan anu asup, timer dimimitian saatos nampi pakét data anu terakhir, ieu kajantenan dina metode ReceivePacket nagara. Assembling
Aktipkeun timer gawé (kaayaan assembling):

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

Henteu aya deui pakét anu sumping dina sambungan anu lebet bari ngantosan timer damel. Timer pareum sareng nyauran metode ProcessPackets, dimana pakét anu leungit dipendakan sareng pamenta pangiriman dikirim pikeun kahiji kalina.
Ngirim pamundut pangiriman deui (kaayaan assembling):

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

Variabel TimerSecondTry disetel ka bener. Variabel ieu tanggung jawab pikeun ngamimitian deui timer kerja.

Di sisi pangirim, pangatur waktu kerja ogé dipicu sareng pakét anu terakhir dikirimkeun deui.
Aktipkeun timer nutup sambungan (kaayaan SendingCycle):

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

Sanggeus éta, timer nutup sambungan dimimitian dina sambungan kaluar.
ReliableUdpState.StartCloseWaitTimer:

protected void StartCloseWaitTimer(ReliableUdpConnectionRecord connectionRecord)
{
  if (connectionRecord.CloseWaitTimer != null)
    connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1);
  else
    connectionRecord.CloseWaitTimer = new Timer(DisposeByTimeout, connectionRecord, connectionRecord.LongTimerPeriod, -1);
}

Periode waktos tutup waktos tutup sambungan nyaéta 30 detik sacara standar.

Sanggeus waktu anu singget, timer gawe di sisi panarima hurung deui, requests dikirim deui, sanggeus éta timer nutup sambungan dimimitian pikeun sambungan asup.

Nalika timer nutup seuneu, sadaya sumberdaya duanana rékaman sambungan dileupaskeun. Pangirim ngalaporkeun kagagalan pangiriman ka aplikasi hulu (tingali UDP API dipercaya).
Ngaleupaskeun sumber rékaman sambungan:

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

Deeper kana kode. Malikkeun mindahkeun data

Diagram pamulihan pangiriman data upami pakét leungitna:Palaksanaan protokol Udp dipercaya pikeun .Net

Sakumaha anu geus dibahas dina nutup sambungan on timeout, nalika timer gawe kadaluwarsa, panarima bakal pariksa pakét leungit. Upami pakét kaleungitan, daptar jumlah pakét anu henteu dugi ka panarima bakal disusun. Jumlah ieu diasupkeun kana LostPackets Asép Sunandar Sunarya sambungan husus, sarta requests pikeun redelivery dikirim.
Ngirim pamundut pikeun ngirimkeun deui bungkusan (kaayaan assembling):

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

Pangirim bakal nampi pamundut pangiriman sareng ngirim pakét anu leungit. Perhatos yén dina waktos ayeuna pangirim parantos ngamimitian waktos nutup sambungan sareng, nalika pamundut ditampi, éta direset.
Ngirimkeun deui pakét anu leungit (kaayaan SendingCycle):

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

Paket ambek (pakét #3 dina diagram) ditampi ku sambungan anu asup. Pamariksaan dilakukeun pikeun ningali naha jandela nampi pinuh sareng pangiriman data normal disimpen deui.
Mariksa hits dina jandela narima (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);
  }
  // ...
}

UDP API dipercaya

Pikeun berinteraksi sareng protokol transfer data, aya kelas Udp Reliable kabuka, anu mangrupikeun bungkus dina blok kontrol transfer. Ieu mangrupikeun anggota kelas anu paling penting:

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

Pesen ditampi ku langganan. Delegasi tanda tangan pikeun metode callback:

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

Pikeun ngalanggan jinis pesen khusus sareng / atanapi pangirim khusus, dua parameter pilihan dianggo: ReliableUdpMessageTypes messageType sareng IPEndPoint ipEndPoint.

Jenis pesen:

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

Suratna dikirim sacara asynchronously; pikeun ieu, protokol ngalaksanakeun modél program asynchronous:

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

Hasil tina ngirim pesen bakal leres - upami pesen suksés dugi ka panarima sareng palsu - upami sambunganna ditutup ku seep:

public bool EndSendMessage(IAsyncResult asyncResult)

kacindekan

Seueur anu teu acan dijelaskeun dina tulisan ieu. Mékanisme thread cocog, iwal jeung penanganan kasalahan, palaksanaan metodeu ngirim pesen Asynchronous. Tapi inti protokol, katerangan ngeunaan logika pikeun ngolah pakét, ngadegkeun sambungan, sarta nanganan timeouts, kudu jelas ka anjeun.

Versi anu nunjukkeun tina protokol pangiriman anu dipercaya cukup kuat sareng fleksibel pikeun nyumponan sarat anu didefinisikeun sateuacana. Tapi abdi hoyong tambahkeun yén palaksanaan dijelaskeun bisa ningkat. Contona, pikeun ngaronjatkeun throughput sarta dinamis ngarobah période timer, mékanisme kayaning jandela ngageser na RTT bisa ditambahkeun kana protokol, éta ogé bakal mangpaat pikeun nerapkeun mékanisme pikeun nangtukeun MTU antara titik sambungan (tapi ngan lamun pesen badag dikirim). .

Hatur nuhun kana perhatosanana, abdi ngarepkeun koméntar sareng koméntar anjeun.

PS Pikeun anu resep kana detil atanapi ngan ukur hoyong nguji protokol, tautan kana proyék di GitHube:
proyék UDP dipercaya

Tumbu mangpaat jeung artikel

  1. spésifikasi protokol TCP: dina basa Inggris и dina basa Rusia
  2. spésifikasi protokol UDP: dina basa Inggris и dina basa Rusia
  3. Diskusi ngeunaan protokol RUDP: draf-ietf-sigtran-dipercaya-udp-00
  4. Protokol Data anu Dipercaya: RFC 908 и RFC 1151
  5. A palaksanaan basajan konfirmasi pangiriman leuwih UDP: Candak Total Kontrol Jaringan Anjeun Jeung .NET Jeung UDP
  6. Artikel ngajéntrékeun mékanisme traversal NAT: Komunikasi Peer-to-Peer Ngaliwatan Panarjamah Alamat Jaringan
  7. Palaksanaan modél programming Asynchronous: Ngalaksanakeun Modél Pemrograman Asynchronous CLR и Kumaha nerapkeun pola desain IAsyncResult
  8. Porting modél program asynchronous kana pola asynchronous dumasar-tugas (APM di TAP):
    TPL jeung Tradisional .NET Asynchronous Programming
    Interop sareng Pola sareng Jinis Asynchronous lianna

Update: Hatur nuhun mayorovp и sidristij pikeun ide pikeun nambihan tugas kana antarmuka. Kasaluyuan perpustakaan jeung sistem operasi heubeul teu dilanggar, sabab Kerangka ka-4 ngadukung server XP sareng 2003.

sumber: www.habr.com

Tambahkeun komentar