Zomangamanga zoyambilira za intaneti zimatengera malo adilesi ofanana momwe node iliyonse inali ndi adilesi yapadziko lonse lapansi komanso yapadera ya IP ndipo imatha kulumikizana mwachindunji ndi ma node ena. Tsopano intaneti, kwenikweni, ili ndi kamangidwe kosiyana - gawo limodzi la ma adilesi a IP padziko lonse lapansi ndi madera ambiri okhala ndi ma adilesi achinsinsi obisika kuseri kwa zida za NAT.M'mapangidwe awa, zida zokha zomwe zili mu adilesi yapadziko lonse lapansi zimatha kulumikizana mosavuta ndi aliyense pamaneti chifukwa ali ndi adilesi yapadera, yosinthika yapadziko lonse lapansi. Node pa intaneti yachinsinsi imatha kulumikizana ndi ma node ena pamaneti omwewo, komanso imatha kulumikizana ndi ma node ena odziwika bwino pamalo adilesi yapadziko lonse lapansi. Kuyanjana uku kumatheka makamaka chifukwa cha njira yomasulira adilesi ya netiweki. Zipangizo za NAT, monga ma router a Wi-Fi, zimapanga zolemba zapadera zomasulira pamalumikizidwe otuluka ndikusintha ma adilesi a IP ndi manambala adoko m'mapaketi. Izi zimalola maulumikizidwe otuluka kuchokera ku netiweki yachinsinsi kupita kwa omwe ali ndi ma adilesi apadziko lonse lapansi. Koma nthawi yomweyo, zida za NAT nthawi zambiri zimatsekereza magalimoto onse omwe akubwera pokhapokha ngati malamulo osiyana olumikizirana akhazikitsidwa.
Kapangidwe ka intaneti kameneka ndi kolondola kokwanira kulumikizana ndi kasitomala ndi seva, komwe makasitomala amatha kukhala pamanetiweki achinsinsi, ndipo ma seva ali ndi adilesi yapadziko lonse lapansi. Koma zimapangitsa kuti pakhale zovuta kugwirizana kwa mfundo ziwiri pakati zosiyanasiyana maukonde achinsinsi. Kulumikizana kwachindunji pakati pa ma node awiri ndikofunikira pamapulogalamu a anzanu ndi anzawo monga kufalitsa mawu (Skype), kupeza mwayi wofikira pakompyuta (TeamViewer), kapena kusewera pa intaneti.
Imodzi mwa njira zothandiza kwambiri zokhazikitsira kulumikizana kwa anzawo ndi anzawo pakati pa zida pamanetiweki osiyanasiyana achinsinsi imatchedwa kubowola mabowo. Njirayi imagwiritsidwa ntchito kwambiri potengera ma protocol a UDP.
Koma ngati ntchito yanu ikufunika kutumizidwa kotsimikizika kwa deta, mwachitsanzo, mumasamutsa mafayilo pakati pa makompyuta, ndiye kuti kugwiritsa ntchito UDP kudzakhala ndi zovuta zambiri chifukwa chakuti UDP si njira yotsimikizirika yobweretsera ndipo sichipereka paketi mwadongosolo, mosiyana ndi TCP. protocol.
Pankhaniyi, kuti mutsimikizire kuperekedwa kwa paketi yotsimikizika, pamafunika kugwiritsa ntchito protocol wosanjikiza yomwe imapereka magwiridwe antchito ndikugwira ntchito pa UDP.
Ndikufuna kuzindikira nthawi yomweyo kuti pali njira yokhomerera dzenje la TCP pokhazikitsa maulumikizidwe a TCP pakati pa ma node pama network osiyanasiyana achinsinsi, koma chifukwa chosowa chithandizo ndi zida zambiri za NAT, nthawi zambiri sizimaganiziridwa ngati njira yayikulu yolumikizira. nodes zotero.
Kwa gawo lotsala la nkhaniyi, ndingoganizira za kukhazikitsidwa kwa protocol yotsimikizika yobweretsera. Kukhazikitsidwa kwa njira yoboola dzenje ya UDP kudzafotokozedwa m'nkhani zotsatirazi.
Kutha kukhazikitsa njira yolamula, ndikutsimikizira uthenga uliwonse
Chigawo choyambirira cha kusamutsa deta pa protocol chiyenera kukhala uthenga
Zofunikira izi makamaka zimagwirizana ndi zofunikira za Reliable Data Protocol zomwe zafotokozedwa mu Ndemanga 908 и Ndemanga 1151, ndipo ndinadalira miyezo imeneyo popanga ndondomekoyi.
Kuti timvetsetse zofunikira izi, tiyeni tiwone nthawi yosinthira deta pakati pa ma node awiri a netiweki pogwiritsa ntchito ma protocol a TCP ndi UDP. Tiyeni muzochitika zonsezi tidzakhala ndi paketi imodzi yotayika. Kusamutsa kwa data yosagwirizana ndi TCP:
Monga momwe mukuonera pa chithunzichi, ngati paketi yatayika, TCP idzazindikira paketi yotayika ndikuwuza wotumizayo pofunsa nambala ya gawo lotayika. Kutumiza kwa data kudzera pa protocol ya UDP:
UDP sitenga njira zodziwira zotayika. Kuwongolera zolakwika zopatsirana mu protocol ya UDP ndi udindo wonse wa pulogalamuyi.
Kuzindikira zolakwika mu protocol ya TCP kumatheka mwa kukhazikitsa kugwirizana ndi mfundo yomaliza, kusunga chikhalidwe cha kugwirizanako, kusonyeza chiwerengero cha ma byte omwe amatumizidwa pamutu uliwonse wa paketi, ndikudziwitsa ma risiti pogwiritsa ntchito nambala yovomerezeka.
Kuonjezera apo, kupititsa patsogolo ntchito (i.e. kutumiza magawo oposa limodzi popanda kuvomereza), protocol ya TCP imagwiritsa ntchito zomwe zimatchedwa zenera lotumizira - chiwerengero cha ma byte a deta omwe wotumiza gawolo akuyembekezera kulandira.
Kuti mumve zambiri za protocol ya TCP, onani Ndemanga 793, kuchokera ku UDP kupita ku Ndemanga 768kumene, kwenikweni, amafotokozedwa.
RequestForPacket - paketi yotsimikizira kapena pempho la paketi yotayika
Mfundo zazikuluzikulu za protocol
Popeza UDP yodalirika imayang'ana kwambiri kufalitsa uthenga wotsimikizika pakati pa ma node awiri, iyenera kukhazikitsa kulumikizana ndi mbali inayo. Kuti akhazikitse kugwirizana, wotumiza amatumiza paketi ndi mbendera ya FirstPacket, yankho lomwe lidzatanthawuza kuti kugwirizana kwakhazikitsidwa. Mapaketi onse oyankhira, kapena, mwa kuyankhula kwina, mapaketi ovomerezeka, nthawi zonse amaika mtengo wa PacketNumber kumunda umodzi woposa mtengo waukulu wa PacketNumber wa mapaketi olandiridwa bwino. The Mungasankhe pa paketi yoyamba kutumizidwa ndi kukula kwa uthenga.
Njira yofananira imagwiritsidwa ntchito kuletsa kulumikizana. Mbendera ya LastPacket imayikidwa pa paketi yomaliza ya uthengawo. Mu paketi yoyankha, nambala ya paketi yomaliza + 1 ikuwonetsedwa, yomwe kumbali yolandirayo imatanthawuza kupereka bwino kwa uthengawo. Kukhazikitsa ndi kutha kwa mgwirizano:
Pamene kugwirizana kukhazikitsidwa, kusamutsa deta kumayamba. Deta imafalitsidwa mu midadada yamapaketi. Chida chilichonse, kupatula chomaliza, chimakhala ndi paketi yokhazikika. Ndizofanana ndi kukula kwazenera kolandila/kutumiza. Deta yomaliza ikhoza kukhala ndi mapaketi ochepa. Pambuyo potumiza chipika chilichonse, mbali yotumiza imadikirira chitsimikiziro chobweretsa kapena pempho loti abweretsenso mapaketi otayika, ndikusiya zenera lolandirira / kutumiza lotseguka kuti lilandire mayankho. Mukalandira chitsimikiziro cha kutumiza kwa block, zenera lolandila / kutumiza limasintha ndipo chipika chotsatira cha data chimatumizidwa.
Mbali yolandirayo imalandira mapaketi. Phukusi lililonse limafufuzidwa kuti liwone ngati likugwera pawindo lopatsira. Paketi ndi zobwereza zomwe sizigwera pazenera zimasefedwa. Chifukwa Ngati kukula kwazenera kumakhazikika komanso kofanana kwa wolandira ndi wotumiza, ndiye kuti pakakhala chipika cha mapaketi operekedwa popanda kutayika, zenera limasinthidwa kuti lilandire mapaketi a chipika chotsatira cha data ndi chitsimikiziro chobweretsa. kutumiza. Ngati zenera silikudzaza mkati mwa nthawi yokhazikitsidwa ndi chowerengera chantchito, ndiye kuti cheke chidzayambika pomwe mapaketi sanaperekedwe ndipo zopempha zotumizidwanso zidzatumizidwa. Chithunzi chotumizanso:
Kutha kwa nthawi ndi zowerengera za protocol
Pali zifukwa zingapo zomwe kulumikizana sikungakhazikitsidwe. Mwachitsanzo, ngati gulu lolandira lilibe intaneti. Pankhaniyi, poyesera kukhazikitsa kugwirizana, kugwirizana kudzatsekedwa ndi nthawi. Kukhazikitsa kodalirika kwa UDP kumagwiritsa ntchito zowerengera ziwiri kukhazikitsa nthawi. Yoyamba, nthawi yogwira ntchito, imagwiritsidwa ntchito kudikirira kuyankha kuchokera kwa omwe ali kutali. Ngati iwotcha mbali yotumiza, ndiye kuti paketi yomaliza yotumizidwayo imakwiyitsidwa. Ngati chowerengera chatha kwa wolandirayo, ndiye kuti cheke cha mapaketi otayika amachitidwa ndipo zopempha zotumiziranso zimatumizidwa.
Nthawi yachiwiri ndiyofunika kutseka kulumikizana ngati palibe kulumikizana pakati pa node. Kwa mbali yotumiza, imayamba nthawi yomweyo itatha nthawi yogwira ntchito, ndikudikirira kuyankha kuchokera ku node yakutali. Ngati palibe yankho mkati mwa nthawi yotchulidwa, kugwirizanako kumathetsedwa ndipo zothandizira zimatulutsidwa. Pa mbali yolandira, cholumikizira chotseka cholumikizira chimayambika pambuyo poti nthawi yantchito yatha kawiri. Izi ndizofunikira kuti mutsimikizire kutayika kwa paketi yotsimikizira. Nthawi yowerengera ikatha, kulumikizanako kumathetsedwanso ndipo zida zimatulutsidwa.
Chithunzi chodalirika cha UDP transmission state
Mfundo za protocol zimayendetsedwa mu makina owerengeka a boma, dera lililonse lomwe limayang'anira malingaliro ena a paketi.
Chithunzi chodalirika cha UDP State:
anatseka - si dziko kwenikweni, ndi poyambira ndi pomaliza kwa automaton. Za boma anatseka chipika chowongolera kufalikira chimalandiridwa, chomwe, pogwiritsa ntchito seva ya UDP yosagwirizana, imatumiza mapaketi kumalumikizidwe oyenera ndikuyamba kukonza boma.
M'chigawochi, paketi yoyamba ya mauthenga abwino imatumizidwa. Kwa mauthenga opanda chitsimikiziro chotumiza, iyi ndi dera lokhalo pomwe uthenga wonse umatumizidwa.
SendingCycle - malo oyambira kutumiza mapaketi a uthenga.
Kusintha kwa izo kuchokera ku boma FirstPacketSending kuchitidwa paketi yoyamba ya uthengawo itatumizidwa. Ndi mumkhalidwe uwu kuti kuvomereza konse ndi zopempha zotumizidwanso zimabwera. Kutulukako n'kotheka muzochitika ziwiri - ngati uthengawo waperekedwa bwino kapena nthawi yomaliza.
FirstPacket Received - chikhalidwe choyambirira kwa wolandira uthengawo.
Imayang'ana kulondola kwa chiyambi cha kufalitsa, imapanga zofunikira, ndikutumiza kuvomereza kulandira paketi yoyamba.
Kwa uthenga womwe uli ndi paketi imodzi ndipo unatumizidwa popanda umboni wa kutumiza, ili ndilo dziko lokhalo. Pambuyo pokonza uthenga wotere, kugwirizanako kumatsekedwa.
Kusonkhana - malo oyambira kulandira mapaketi a uthenga.
Imalemba mapaketi kusungirako kwakanthawi, imayang'ana kutayika kwa paketi, imatumiza chivomerezo cha kuperekedwa kwa chipika cha mapaketi ndi uthenga wonse, ndikutumiza zopempha kuti zibweretsenso mapaketi otayika. Ngati uthenga wonse walandira bwino, kulumikizana kumapita ku boma Zatha, apo ayi, nthawi yomaliza imachoka.
Dzikoli ndilofunika pa msonkhano wa uthenga komanso pamlanduwo pamene chitsimikiziro cha uthengawo chinatayika panjira yopita kwa wotumiza. Derali limatuluka ndi nthawi, koma kulumikizidwa kumawonedwa kuti kwatsekedwa bwino.
Kuzama mu code. transmission control unit
Chimodzi mwazinthu zazikulu za UDP yodalirika ndi block control block. Ntchito ya chipikachi ndikusunga maulumikizidwe apano ndi zinthu zothandizira, kugawa mapaketi omwe akubwera kumalumikizidwe ofananira, kupereka mawonekedwe otumizira mapaketi kulumikizano, ndikukhazikitsa API ya protocol. Chida chowongolera ma transmission chimalandira mapaketi kuchokera pagawo la UDP ndikuwapititsa kumakina aboma kuti akakonze. Kuti ilandire mapaketi, imagwiritsa ntchito seva ya UDP yosasinthika. Mamembala ena a kalasi ya 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;
//...
}
Kukhazikitsa seva ya UDP yosasinthika:
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);
}
Pakutumiza uthenga uliwonse, chimapangidwa chomwe chimakhala ndi chidziwitso chokhudza kulumikizana. Mapangidwe oterowo amatchedwa mbiri yolumikizana. Mamembala ena agulu la 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;
//...
}
Kuzama mu code. limati
Mayiko amagwiritsira ntchito makina a boma a Reliable UDP protocol, kumene kukonza kwakukulu kwa mapaketi kumachitika. Gulu la abstract ReliableUdpState limapereka mawonekedwe a boma:
Lingaliro lonse la protocol likuyendetsedwa ndi makalasi omwe aperekedwa pamwambapa, pamodzi ndi gulu lothandizira lomwe limapereka njira zosasunthika, monga, mwachitsanzo, kupanga mutu wa ReliableUdp kuchokera ku mbiri yolumikizira.
Kenako, tiwona mwatsatanetsatane kukhazikitsa njira zolumikizirana zomwe zimatsimikizira ma aligorivimu oyambira a protocol.
Njira ya DisposeByTimeout
Njira ya DisposeByTimeout ili ndi udindo wotulutsa zida zolumikizirana pakatha nthawi komanso kuwonetsa mauthenga opambana / osapambana. ReliableUdpState.DisposeByTimeout:
protected override void DisposeByTimeout(object record)
{
ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) record;
// сообщаем об успешном получении сообщения
SetAsCompleted(connectionRecord);
}
Njira ya ProcessPackets
Njira ya ProcessPackets imayang'anira kukonza kowonjezera kwa phukusi kapena phukusi. Idayitanidwa mwachindunji kapena kudzera pa paketi yodikirira nthawi.
Wokhoza Kusonkhana njirayo ndi yolemetsa ndipo ili ndi udindo wofufuza mapaketi otayika ndikusintha kupita ku boma Zatha, ngati mutalandira paketi yomaliza ndikudutsa cheke chopambana 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);
}
}
Wokhoza SendingCycle njira iyi imatchedwa kokha pa timer, ndipo ali ndi udindo resending otsiriza uthenga, komanso kuthandizira kugwirizana pafupi nthawi. 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);
}
public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
if (connectionRecord.WaitForPacketsTimer != null)
connectionRecord.WaitForPacketsTimer.Dispose();
// собираем сообщение и передаем его подписчикам
ReliableUdpStateTools.CreateMessageFromMemoryStream(connectionRecord);
}
ReceivePacket Njira
Wokhoza FirstPacket Received ntchito yaikulu ya njira ndi kudziwa ngati uthenga paketi woyamba anafikadi pa mawonekedwe, komanso kusonkhanitsa uthenga wopangidwa ndi paketi imodzi. Paketi Yoyamba Yalandiridwa.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);
}
}
Wokhoza SendingCycle njira iyi imachotsedwa kuti ivomereze kuvomereza kutumizidwa ndi zopempha zotumizanso. 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));
}
Wokhoza Kusonkhana mu njira ya ReceivePacket, ntchito yayikulu yosonkhanitsa uthenga kuchokera pamapaketi obwera ikuchitika. 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);
}
}
Wokhoza Zatha ntchito yokhayo ya njirayo ndikutumiza kuvomerezanso kwabwino kwa uthengawo. Yamaliza.ReceivePacket:
public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
// повторная отправка последнего пакета в связи с тем,
// что последний ack не дошел до отправителя
if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
{
ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
}
}
Tumizani Packet Njira
Wokhoza FirstPacketSending njira iyi imatumiza paketi yoyamba ya deta, kapena, ngati uthenga sufuna kutsimikizira yobereka, uthenga wonse. 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);
}
Wokhoza SendingCycle mwa njira iyi, chipika cha mapaketi chimatumizidwa. 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 );
}
}
Kuzama mu code. Kupanga ndi Kukhazikitsa Zogwirizana
Taganizirani mwatsatanetsatane chilengedwe mbiri yolumikizana kulumikiza ndi kutumiza paketi yoyamba. Kusamutsa nthawi zonse kumayambitsidwa ndi pulogalamu yomwe imayitanitsa API ya uthenga. Kenako, njira ya StartTransmission ya block control yotumizira imapemphedwa, yomwe imayamba kutumiza deta ya uthenga watsopano. Kupanga mgwirizano wotuluka:
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]);
}
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);
}
}
Kuzama mu code. Kutseka kulumikiza panthawi yake
Kusamalira nthawi ndi gawo lofunikira la UDP yodalirika. Ganizirani chitsanzo chomwe nodi yapakatikati idalephera ndipo kutumiza deta mbali zonse ziwiri kudakhala kosatheka. Chithunzi chotseka kulumikizana ndi nthawi:
Monga momwe tikuonera pachithunzichi, chowerengera cha wotumiza chimayamba atangotumiza chipika cha mapaketi. Izi zimachitika mu SendPacket njira ya boma SendingCycle. Kuyatsa chowerengera chantchito (SendingCycle state):
public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{
// отправляем блок пакетов
// ...
// перезапускаем таймер после отправки
connectionRecord.WaitForPacketsTimer.Change( connectionRecord.ShortTimerPeriod, -1 );
if ( connectionRecord.CloseWaitTimer != null )
connectionRecord.CloseWaitTimer.Change( -1, -1 );
}
Nthawi zowerengera zimayikidwa pomwe kulumikizidwa kwapangidwa. The default ShortTimerPeriod ndi 5 masekondi. Mu chitsanzo, izo zakhazikitsidwa 1,5 masekondi.
Pakulumikiza komwe kukubwera, chowerengera chimayamba mutalandira paketi yomaliza ya data, izi zimachitika munjira ya ReceivePacket ya boma. Kusonkhana Kuyang'anira nthawi yogwirira ntchito (State 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);
}
}
public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
// ...
// отправляем повторно последний пакет
// ...
// включаем таймер CloseWait – для ожидания восстановления соединения или его завершения
StartCloseWaitTimer(connectionRecord);
}
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));
}
Paketi yotumizidwa (paketi #3 pazithunzi) imalandiridwa ndi kulumikizana komwe kukubwera. Cheke imapangidwa kuti muwone ngati zenera lolandirira ladzaza ndipo kutumiza kwa data kwachilengedwe kumabwezeretsedwa. Kuyang'ana kugunda pawindo lolandirira (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);
}
// ...
}
API yodalirika ya UDP
Kuti mulumikizane ndi protocol yosinthira deta, pali gulu lotseguka lodalirika la Udp, lomwe limakutira pa block control block. Nawa mamembala ofunikira kwambiri m'kalasi:
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()
}
Mauthenga amalandiridwa polembetsa. Perekani siginecha ya njira yobwereza:
public delegate void ReliableUdpMessageCallback( ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteClient );
Uthenga:
public class ReliableUdpMessage
{
// тип сообщения, простое перечисление
public ReliableUdpMessageTypes Type { get; private set; }
// данные сообщения
public byte[] Body { get; private set; }
// если установлено в true – механизм подтверждения доставки будет отключен
// для передачи конкретного сообщения
public bool NoAsk { get; private set; }
}
Kuti mulembetse ku mtundu wina wa uthenga ndi/kapena wotumiza wina, magawo awiri osankha amagwiritsidwa ntchito: ReliableUdpMessageTypes messageType ndi IPEndPoint ipEndPoint.
Mitundu ya mauthenga:
public enum ReliableUdpMessageTypes : short
{
// Любое
Any = 0,
// Запрос к STUN server
StunRequest = 1,
// Ответ от STUN server
StunResponse = 2,
// Передача файла
FileTransfer =3,
// ...
}
Zosintha: Zikomo mayorovp и sidristij kwa lingaliro lowonjezera ntchito pa mawonekedwe. Kugwirizana kwa laibulale ndi machitidwe akale ogwiritsira ntchito sikuphwanyidwa, chifukwa Chigawo chachinayi chimathandizira onse XP ndi seva ya 4.