ʻO ka hoʻolālā kumu o ka Pūnaewele i manaʻo ʻia he wahi kikoʻī like ʻole i loaʻa i kēlā me kēia node kahi IP IP honua a kū hoʻokahi a hiki ke kamaʻilio pololei me nā node ʻē aʻe. I kēia manawa, he ʻano hana ʻokoʻa ka Pūnaewele - hoʻokahi wahi o nā IP IP honua a me nā wahi he nui me nā helu pilikino i hūnā ʻia ma hope o nā polokalamu NAT.I loko o kēia hoʻolālā, hiki i nā mea hana wale nō i loko o ka wahi kikoʻī honua ke kamaʻilio maʻalahi me kekahi ma ka pūnaewele no ka mea he kūlana IP kūʻokoʻa ko lākou. Hiki i kahi node ma kahi pūnaewele pilikino ke hoʻohui i nā node ʻē aʻe ma ka pūnaewele hoʻokahi, a hiki ke hoʻopili pū i nā node kaulana ʻē aʻe ma ka wahi helu wahi honua. Loaʻa ʻia kēia pilina ma muli o ke ʻano unuhi ʻōlelo helu pūnaewele. Na nā mea NAT, e like me nā mea hoʻokele Wi-Fi, hana i nā hoʻokomo papaʻaina unuhi kūikawā no nā pilina i waho a hoʻololi i nā helu IP a me nā helu awa ma nā ʻeke. Hāʻawi kēia i nā pilina i waho mai ka pūnaewele pilikino i nā mea hoʻokipa ma ka wahi kikoʻī honua. Akā i ka manawa like, hoʻopaʻa maʻamau nā mea NAT i nā kaʻa komo a pau ke ʻole i hoʻonohonoho ʻia nā lula kaʻawale no nā pili komo.
Ua kūpono kēia hoʻolālā o ka Pūnaewele no ke kamaʻilio ʻana i nā mea kūʻai aku, kahi e hiki ai i nā mea kūʻai aku ma nā pūnaewele pilikino, a loaʻa i nā kikowaena kahi helu honua. Akā, hana ia i nā pilikia no ka hoʻopili pololei ʻana o nā nodes ʻelua ma waena ʻokoʻa nā pūnaewele pilikino. He mea nui ka pilina pololei ma waena o nā node ʻelua no nā noi hoa-a-peer e like me ka hoʻouna leo (Skype), ka loaʻa ʻana o kahi mamao i kahi kamepiula (TeamViewer), a i ʻole nā pāʻani pūnaewele.
ʻO kekahi o nā ala maikaʻi loa no ka hoʻokumu ʻana i kahi pilina peer-to-peer ma waena o nā polokalamu ma nā pūnaewele pilikino like ʻole i kapa ʻia ʻo hole punching. Hoʻohana pinepine ʻia kēia ʻenehana me nā noi e pili ana i ka protocol UDP.
Akā inā makemake kāu noi i ka hāʻawi ʻana i ka ʻikepili, no ka laʻana, hoʻololi ʻoe i nā faila ma waena o nā kamepiula, a laila e nui nā pilikia o ka hoʻohana ʻana i ka UDP ma muli o ka ʻoiaʻiʻo ʻaʻole ʻo UDP kahi protocol hāʻawi i hōʻoiaʻiʻo ʻia a ʻaʻole hāʻawi i ka hāʻawi ʻana i ka packet ma ke ʻano, ʻaʻole like me ka TCP protocol.
I kēia hihia, no ka hōʻoia ʻana i ka hāʻawi ʻana i ka packet, pono ia e hoʻokō i kahi protocol layer application e hāʻawi ana i ka hana pono a me nā hana ma luna o UDP.
Makemake au e hoʻomaopopo koke aia kahi TCP hole punching technique no ka hoʻokumu ʻana i nā pilina TCP ma waena o nā nodes i nā ʻoihana pilikino like ʻole, akā ma muli o ke kākoʻo ʻole ʻia e nā mea he nui NAT, ʻaʻole i manaʻo ʻia ʻo ia ke ala nui e hoʻopili ai. ia mau node.
No ke koena o kēia ʻatikala, e kālele wale wau i ka hoʻokō ʻana i ka protocol delivery guaranteed. E wehewehe ʻia ka hoʻokō ʻana i ka ʻenehana hole punching UDP ma nā ʻatikala aʻe.
Nā Koina Kūkākūkā
Hoʻokō ʻia ka hāʻawi ʻana i ka ʻeke paʻa ma o ke ʻano manaʻo manaʻo maikaʻi (ʻo ia ka mea i kapa ʻia he hōʻoia maikaʻi)
ʻO ka pono no ka hoʻololi kūpono o ka ʻikepili nui, ʻo ia. Pono ka protocol e pale i ka hoʻokuʻu ʻana i ka packet pono ʻole
Hiki ke hoʻopau i ka mīkini hōʻoia hōʻoia (ka hiki ke hana ma ke ʻano he "pure" UDP protocol)
Hiki ke hoʻokō i ke ʻano kauoha, me ka hōʻoia ʻana o kēlā me kēia memo
ʻO ka ʻāpana kumu o ka hoʻoili ʻikepili ma luna o ka protocol he memo
Kūlike nui kēia mau koi me nā koi Reliable Data Protocol i wehewehe ʻia ma RFC 908 и RFC 1151, a ua hilinaʻi au i kēlā mau kūlana i ka hoʻomohala ʻana i kēia protocol.
No ka hoʻomaopopo ʻana i kēia mau koi, e nānā kākou i ka manawa o ka hoʻoili ʻana i ka ʻikepili ma waena o nā node pūnaewele ʻelua me ka hoʻohana ʻana i nā protocol TCP a me UDP. E ʻae i nā hihia ʻelua e nalowale mākou i hoʻokahi ʻeke. Ka hoʻoili ʻana o ka ʻikepili pili ʻole ma luna o TCP:
E like me kāu e ʻike ai mai ke kiʻikuhi, inā nalowale ka ʻeke, e ʻike ʻo TCP i ka ʻeke nalowale a hōʻike i ka mea hoʻouna ma ke noi ʻana i ka helu o ka māhele nalowale. Ka hoʻoili ʻikepili ma o ka protocol UDP:
ʻAʻole lawe ʻo UDP i nā hana ʻike pohō. ʻO ka mālama ʻana i nā hewa o ka hoʻouna ʻana i ka protocol UDP ke kuleana o ka noi.
Loaʻa ka ʻike hewa i ka protocol TCP ma ka hoʻokumu ʻana i kahi pilina me kahi node hopena, mālama i ke kūlana o kēlā pili, e hōʻike ana i ka helu o nā bytes i hoʻouna ʻia i kēlā me kēia poʻomanaʻo packet, a me ka hoʻolaha ʻana i nā loaʻa me ka hoʻohana ʻana i ka helu hōʻoia.
Eia hou, no ka hoʻomaikaʻi ʻana i ka hana (ʻo ia hoʻi, e hoʻouna ʻoi aku ma mua o hoʻokahi ʻāpana me ka loaʻa ʻole o ka hōʻoia), hoʻohana ka protocol TCP i ka puka makani i kapa ʻia - ka helu o nā bytes o ka ʻikepili i manaʻo ʻia e loaʻa i ka mea hoʻouna o ka māhele.
No ka ʻike hou aku e pili ana i ka protocol TCP, e ʻike RFC 793, mai UDP a i RFC 768kahi i wehewehe ʻia ai.
Mai ka mea i luna aʻe, ua akaka ʻia i mea e hana ai i kahi protocol hoʻouna memo hilinaʻi ma luna o UDP (i ʻōlelo ʻia ma hope nei UDP hilinaʻi), pono e hoʻokō i nā mīkini hoʻoili ʻikepili e like me TCP. ʻO ia hoʻi:
mālama i ke kūlana pili
hoʻohana i ka helu ʻāpana
hoʻohana i nā pūʻolo hōʻoia kūikawā
e hoʻohana i kahi mīkini puka makani maʻalahi e hoʻonui i ka throughput protocol
Eia hou, pono ʻoe:
hōʻailona i ka hoʻomaka ʻana o kahi leka, e hoʻokaʻawale i nā kumuwaiwai no ka pilina
hōʻailona i ka pau ʻana o kahi memo, e hāʻawi i ka memo i loaʻa i ka noi upstream a hoʻokuʻu i nā kumuwaiwai protocol
e ʻae i ka protocol pili pili e hoʻopau i ka mīkini hōʻoia hōʻoia e hana ma ke ʻano he UDP "maʻemaʻe".
Poʻomanaʻo UDP hilinaʻi
E hoʻomanaʻo ua hoʻopili ʻia kahi datagram UDP i kahi datagram IP. Hoʻopili ʻia ka ʻeke UDP hilinaʻi i loko o kahi datagram UDP. Hoʻopili poʻomanaʻo UDP hilinaʻi:
He mea maʻalahi ke ʻano o ke poʻo UDP hilinaʻi:
Hae - hae hoomalu puolo
MessageType - ke ʻano memo i hoʻohana ʻia e nā noi upstream e kau inoa i nā memo kikoʻī
TransmissionId - ka helu o ka hoʻouna ʻana, me ka helu wahi a me ke awa o ka mea i loaʻa, ʻike kūʻokoʻa i ka pilina
PacketNumber - helu packet
Nā koho - nā koho protocol hou. I ka hihia o ka ʻeke mua, hoʻohana ʻia ia e hōʻike i ka nui o ka memo
Penei ka hae.
FirstPacket - ka ʻeke mua o ka memo
NoAsk - ʻaʻole koi ka memo i kahi hana hoʻomaopopo e hiki ai
LastPacket - ka ʻeke hope o ka memo
RequestForPacket - ʻeke hōʻoia a i ʻole noi no kahi ʻeke nalowale
Nā loina maʻamau o ka protocol
Ma muli o ka hilinaʻi ʻana o ka UDP hilinaʻi i ka hoʻouna ʻana i ka memo ma waena o nā nodes ʻelua, pono ia e hoʻokumu i kahi pilina me kēlā ʻaoʻao. No ka hoʻokumu ʻana i kahi pilina, hoʻouna ka mea hoʻouna i kahi ʻeke me ka hae FirstPacket, ʻo ka pane e manaʻo ʻia ua hoʻokumu ʻia ka pilina. ʻO nā ʻeke pane a pau, a i ʻole, ʻo ia hoʻi, nā ʻeke hōʻoia, e hoʻonoho mau i ka waiwai o ka pā PacketNumber i hoʻokahi ʻoi aku ma mua o ka waiwai nui o ka PacketNumber o nā ʻeke i loaʻa kūleʻa. ʻO ke kahua koho no ka ʻeke mua i hoʻouna ʻia ka nui o ka memo.
Hoʻohana ʻia kahi mīkini like e hoʻopau i kahi pilina. Hoʻonoho ʻia ka hae LastPacket ma ka ʻeke hope o ka memo. Ma ka ʻeke pane, hōʻike ʻia ka helu o ka ʻeke hope + 1, ʻo ia hoʻi no ka ʻaoʻao e loaʻa ana ke ʻano o ka lawe ʻana i ka leka. Ka hoʻokumu ʻana i ka pilina a me ke kiʻikuhi hoʻopau:
Ke hoʻokumuʻia ka pilina, hoʻomaka ka hoʻoiliʻikepili. Hoʻouna ʻia ka ʻikepili ma nā poloka o nā ʻeke. Loaʻa i kēlā me kēia poloka, koe ka mea hope loa, he helu paʻa o nā ʻeke. Ua like ia me ka nui pukaaniani loaa/hoouna. He liʻiliʻi paha nā ʻeke o ka ʻikepili hope loa. Ma hope o ka hoʻouna ʻana i kēlā me kēia poloka, kali ka ʻaoʻao hoʻouna no ka hōʻoia hoʻouna ʻana a i ʻole kahi noi e hoʻouna hou i nā ʻeke i nalowale, e waiho ana i ka puka hoʻokipa / hoʻouna e hāmama no ka loaʻa ʻana o nā pane. Ma hope o ka loaʻa ʻana o ka hōʻoia o ka hāʻawi ʻana i ka poloka, hoʻololi ka puka makani loaʻa / hoʻouna ʻia a hoʻouna ʻia ka poloka o ka ʻikepili.
Loaʻa i ka ʻaoʻao e loaʻa nā ʻeke. Nānā ʻia kēlā me kēia ʻeke e ʻike inā hāʻule i loko o ka puka makani hoʻouna. Hoʻopili ʻia nā ʻeke a me nā kope i hāʻule ʻole i ka puka makani. No ka mea Inā hoʻopaʻa ʻia ka nui o ka puka makani a like no ka mea i loaʻa a me ka mea hoʻouna, a laila i ka hihia o kahi poloka o nā ʻeke i hāʻawi ʻia me ka nalowale ʻole, ua hoʻoneʻe ʻia ka puka makani e loaʻa i nā ʻeke o ka poloka o ka ʻikepili aʻe a ʻo ka hōʻoia hoʻouna ʻana. hoounaia. Inā ʻaʻole i piha ka puka aniani i loko o ka manawa i hoʻonohonoho ʻia e ka mea hana manawa hana, a laila e hoʻomaka ʻia kahi māka i hāʻawi ʻole ʻia nā ʻeke a hoʻouna ʻia nā noi no ka hoʻouna hou ʻana. Kiʻi hoʻouna hou:
Hoʻopau manawa a me nā manawa protocol
Nui nā kumu i hiki ʻole ke hoʻokumu ʻia kahi pilina. No ka laʻana, inā pahemo ka pāʻina e loaʻa ana. I kēia hihia, i ka wā e hoʻāʻo ai e hoʻokumu i kahi pilina, e pani ʻia ka pilina e ka manawa pau. Hoʻohana ka hoʻokō UDP hilinaʻi i ʻelua mau manawa e hoʻonohonoho i nā manawa. ʻO ka mea mua, ka manawa hana, hoʻohana ʻia e kali i kahi pane mai ka host mamao. Inā ʻā ia ma ka ʻaoʻao o ka mea hoʻouna, a laila hoʻouna ʻia ka ʻeke hope i hoʻouna ʻia. Inā pau ka manawa i ka mea loaʻa, a laila e hana ʻia kahi māka no nā ʻeke i nalowale a hoʻouna ʻia nā noi no ka hoʻouna hou ʻana.
Pono ka lua o ka manawa e pani i ka pilina inā ʻaʻohe kamaʻilio ma waena o nā nodes. No ka ʻaoʻao hoʻouna, hoʻomaka koke ia ma hope o ka pau ʻana o ka manawa hana, a kali i ka pane mai ka node mamao. Inā ʻaʻohe pane i loko o ka manawa i ʻōlelo ʻia, hoʻopau ʻia ka pilina a hoʻokuʻu ʻia nā kumuwaiwai. No ka ʻaoʻao e loaʻa ana, hoʻomaka ka manawa pili pili ma hope o ka pau ʻana o ka manawa hana ʻelua. Pono kēia e hōʻoia i ka nalowale o ka ʻeke hōʻoia. Ke pau ka manawa, hoʻopau ʻia ka pilina a hoʻokuʻu ʻia nā kumuwaiwai.
Kiʻi mokuʻāina hoʻouna UDP hilinaʻi
Hoʻokomo ʻia nā loina o ka protocol i kahi mīkini mokuʻāina palena ʻole, ʻo kēlā me kēia mokuʻāina ke kuleana no kekahi loiloi o ka hoʻoili ʻana i ka packet.
Kiʻi Mokuʻāina UDP hilinaʻi:
pani ' - ʻaʻole ia he mokuʻāina, he wahi hoʻomaka a hoʻopau no ka automaton. No ka moku'āina pani ' Loaʻa ka poloka mana hoʻouna, kahi e hoʻokō ai i kahi kikowaena UDP asynchronous, e hoʻouna i nā ʻeke i nā pilina kūpono a hoʻomaka i ka hana mokuʻāina.
FirstPacketSending - ka moku'āina mua kahi i hoʻouna ʻia ai ka leka.
Ma kēia mokuʻāina, hoʻouna ʻia ka ʻeke mua no nā memo maʻamau. No nā memo me ka hoʻouna ʻole ʻana i ka hōʻoia, ʻo kēia wale nō ka mokuʻāina kahi i hoʻouna ʻia ai ka leka holoʻokoʻa.
SendingCycle - ke kūlana honua no ka hoʻouna ʻana i nā ʻeke memo.
Ke hoʻololi iā ia mai ka moku'āina FirstPacketSending hana ʻia ma hope o ka hoʻouna ʻia ʻana o ka ʻeke mua o ka memo. Aia ma kēia moku'āina e hiki mai ai nā ʻae a me nā noi no ka hoʻouna hou ʻana. Hiki ke puka i waho i ʻelua mau hihia - i ka hihia o ka hoʻopuka kūleʻa ʻana o ka memo a i ʻole ma ka manawa pau.
Loaʻa ʻia ʻo FirstPacket - ke kūlana mua no ka mea i loaʻa ka leka.
Nānā ia i ka pololei o ka hoʻomaka ʻana o ka hoʻouna ʻana, hana i nā hale kūpono, a hoʻouna i kahi hōʻoia no ka loaʻa ʻana o ka ʻeke mua.
No kahi leka i loaʻa i hoʻokahi ʻeke a hoʻouna ʻia me ka hoʻohana ʻole ʻana i ka hōʻoia o ka lawe ʻana, ʻo ia wale nō ka mokuʻāina. Ma hope o ka hana ʻana i kēlā memo, ua pani ʻia ka pilina.
Hoʻohui - kūlana kumu no ka loaʻa ʻana o nā ʻeke memo.
Kākau ʻo ia i nā ʻeke i ka waiho ʻana no ka manawa pōkole, nānā no ka nalowale ʻana o ka ʻeke, hoʻouna i nā hōʻoia no ka lawe ʻana i kahi poloka o nā ʻeke a me ka memo holoʻokoʻa, a hoʻouna i nā noi no ka lawe hou ʻana i nā ʻeke i nalowale. I ka loaʻa ʻana o ka memo holoʻokoʻa, hele ka pilina i ka mokuʻāina pau, inā ʻaʻole, e puka ka manawa pau.
pau - pani i ka pilina inā loaʻa ka memo holoʻokoʻa.
Pono kēia moku'āina no ka huiʻana o ka leka a no ka hihia i ka wā i nalowale ai ka hōʻoiaʻana o ka leka i ke ala i ka mea hoʻouna. Hoʻopau ʻia kēia mokuʻāina me ka manawa pau, akā ua manaʻo ʻia ua pani ʻia ka pilina.
Hohonu i ke code. ʻāpana hoʻokele hoʻoili
ʻO kekahi o nā mea koʻikoʻi o ka UDP hilinaʻi ʻo ia ka poloka mana hoʻouna. ʻO ka hana o kēia poloka ʻo ia ka mālama ʻana i nā pilina o kēia manawa a me nā mea kōkua, e puʻunaue i nā ʻeke komo i nā pilina pili, e hāʻawi i kahi interface no ka hoʻouna ʻana i nā ʻeke i kahi pilina, a hoʻokō i ka protocol API. Loaʻa ka poloka mana hoʻouna i nā ʻeke mai ka papa UDP a hoʻouna iā lākou i ka mīkini mokuʻāina no ka hana ʻana. No ka loaʻa ʻana o nā ʻeke, hoʻokō ia i kahi kikowaena UDP asynchronous. ʻO kekahi mau lālā o ka papa 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;
//...
}
Ka hoʻokō ʻana i kahi kikowaena UDP asynchronous:
private void Receive()
{
EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0);
// создаем новый буфер, для каждого socket.BeginReceiveFrom
byte[] buffer = new byte[DefaultMaxPacketSize + ReliableUdpHeader.Length];
// передаем буфер в качестве параметра для асинхронного метода
this.m_socketIn.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref connectedClient, EndReceive, buffer);
}
private void EndReceive(IAsyncResult ar)
{
EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = this.m_socketIn.EndReceiveFrom(ar, ref connectedClient);
//пакет получен, готовы принимать следующий
Receive();
// т.к. простейший способ решить вопрос с буфером - получить ссылку на него
// из IAsyncResult.AsyncState
byte[] bytes = ((byte[]) ar.AsyncState).Slice(0, bytesRead);
// получаем заголовок пакета
ReliableUdpHeader header;
if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header))
{
// пришел некорректный пакет - отбрасываем его
return;
}
// конструируем ключ для определения connection record’а для пакета
Tuple<EndPoint, Int32> key = new Tuple<EndPoint, Int32>(connectedClient, header.TransmissionId);
// получаем существующую connection record или создаем новую
ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header.ReliableUdpMessageType));
// запускаем пакет в обработку в конечный автомат
record.State.ReceivePacket(record, header, bytes);
}
No kēlā me kēia hoʻoili memo, hana ʻia kahi hale e loaʻa ai ka ʻike e pili ana i ka pilina. Kapa ʻia kēlā ʻano hale mooolelo pili. ʻO kekahi mau lālā o ka papa 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;
//...
}
Hohonu i ke code. mokuʻāina
Hoʻokō nā mokuʻāina i ka mīkini mokuʻāina o ka protocol UDP hilinaʻi, kahi e hana ai ka hana nui o nā ʻeke. Hāʻawi ka papa abstract ReliableUdpState i kahi kikowaena no ka mokuʻāina:
Hoʻokō ʻia ka loiloi holoʻokoʻa o ka protocol e nā papa i hōʻike ʻia ma luna, me kahi papa kōkua e hāʻawi ana i nā ʻano static, e like me, no ka laʻana, ke kūkulu ʻana i ke poʻo ReliableUdp mai ka moʻolelo pili.
Ma hope aʻe, e noʻonoʻo pono mākou i ka hoʻokō ʻana i nā ʻano o ka interface e hoʻoholo ai i nā algorithm kumu o ka protocol.
Ke ala DisposeByTimeout
ʻO ke ala DisposeByTimeout ke kuleana no ka hoʻokuʻu ʻana i nā kumuwaiwai pili ma hope o ka manawa pau a no ka hōʻailona ʻana i ka hoʻouna ʻana i ka memo i kūleʻa/pono ʻole. ReliableUdpState.DisposeByTimeout:
Hoʻopau wale ʻia ma ka mokuʻāina pau. Hoʻopau.DisposeByTimeout:
protected override void DisposeByTimeout(object record)
{
ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) record;
// сообщаем об успешном получении сообщения
SetAsCompleted(connectionRecord);
}
Kaʻina ProcessPackets
ʻO ke ala ProcessPackets ke kuleana no ka hana hou ʻana o kahi pūʻolo a i ʻole nā pūʻolo. Kāhea pololei ʻia a i ʻole ma o ka manawa kali packet.
hiki Hoʻohui Ua hoʻopau ʻia ke ʻano hana a nona ke kuleana no ka nānā ʻana i nā ʻeke nalowale a me ka hoʻololi ʻana i ka mokuʻāina pau, i ka loaʻa ʻana o ka ʻeke hope loa a hāʻawi i kahi māka kūleʻa 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);
}
}
hiki SendingCycle Ua kapa ʻia kēia ʻano ma ka manawa, a ʻo ia ke kuleana no ka hoʻouna hou ʻana i ka memo hope loa, a me ka hiki ʻana i ka pili pili. 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);
}
hiki pau hoʻopau ke ala i ka manawa holo a hoʻouna i ka leka i ka poʻe kākau inoa. Ua pau.
public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
if (connectionRecord.WaitForPacketsTimer != null)
connectionRecord.WaitForPacketsTimer.Dispose();
// собираем сообщение и передаем его подписчикам
ReliableUdpStateTools.CreateMessageFromMemoryStream(connectionRecord);
}
Loaʻa i ke ala
hiki Loaʻa ʻia ʻo FirstPacket ʻO ka hana nui o ke ʻano, ʻo ia ka hoʻoholo inā ua hōʻea maoli ka ʻeke memo mua i ka interface, a me ka hōʻiliʻili ʻana i kahi memo i hoʻokahi ʻeke. 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);
}
}
hiki SendingCycle Hoʻopau ʻia kēia ʻano hana no ka ʻae ʻana i nā ʻae hoʻouna a me nā noi hoʻouna hou. 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));
}
hiki Hoʻohui ma ke ʻano ReceivePacket, ʻo ka hana nui o ka hōʻuluʻulu ʻana i kahi leka mai nā ʻeke komo mai. Hoʻohui. Loaʻa i kaPacket:
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);
}
}
hiki pau ʻO ka hana wale nō o ke ʻano, ʻo ia ka hoʻouna ʻana i kahi hōʻoia hou i ka hoʻopuka kūleʻa o ka leka. Hoʻopau. Loaʻa i kaPacket:
public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
// повторная отправка последнего пакета в связи с тем,
// что последний ack не дошел до отправителя
if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
{
ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
}
}
Hoʻouna i ke ʻano o ka ʻeke
hiki FirstPacketSending Hoʻouna kēia ʻano i ka ʻeke ʻikepili mua, a inā ʻaʻole koi ka leka i ka hōʻoia hoʻouna, ʻo ka memo holoʻokoʻa. 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);
}
hiki SendingCycle ma kēia ʻano, hoʻouna ʻia kahi poloka o nā ʻeke. 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 );
}
}
Hohonu i ke code. Hoʻokumu a hoʻokumu i nā pilina
I kēia manawa ua ʻike mākou i nā mokuʻāina kumu a me nā ʻano i hoʻohana ʻia no ka mālama ʻana i nā mokuʻāina, e wāwahi mākou i kekahi mau hiʻohiʻona o ke ʻano o ka hana ʻana o ka protocol i kahi kikoʻī. Kiʻi hoʻoili ʻikepili ma lalo o nā kūlana maʻamau:
E noʻonoʻo pono i ka hana ʻana mooolelo pili e hoʻohui a hoʻouna i ka ʻeke mua. Hoʻomaka mau ʻia ka hoʻoili ʻana e ka noi e kāhea ana i ka API hoʻouna leka. A laila, kāhea ʻia ke ʻano StartTransmission o ka poloka mana hoʻouna, e hoʻomaka ana i ka lawe ʻana i ka ʻikepili no ka memo hou. Ke hana nei i kahi pilina i waho:
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]);
}
Hoʻouna i ka ʻeke mua (FirstPacketSending state):
public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{
connectionRecord.PacketCounter = 0;
connectionRecord.SndNext = 0;
connectionRecord.WindowLowerBound = 0;
// ...
// создаем заголовок пакета и отправляем его
ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));
// увеличиваем счетчик
connectionRecord.SndNext++;
// сдвигаем окно
connectionRecord.WindowLowerBound++;
// переходим в состояние SendingCycle
connectionRecord.State = connectionRecord.Tcb.States.SendingCycle;
// Запускаем таймер
connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1);
}
Ma hope o ka hoʻouna ʻana i ka ʻeke mua, komo ka mea hoʻouna i ka mokuʻāina SendingCycle - kali no ka hōʻoia ʻana o ka lawe ʻana i ka pūʻolo.
ʻO ka ʻaoʻao loaʻa, me ka hoʻohana ʻana i ke ʻano EndReceive, loaʻa ka ʻeke i hoʻouna ʻia, hana i kahi mea hou mooolelo pili a hāʻawi i kēia ʻeke, me kahi poʻomanaʻo i hoʻopaʻa mua ʻia, i ke ʻano ReceivePacket o ka mokuʻāina no ka hana ʻana. Loaʻa ʻia ʻo FirstPacket Ke hana ʻana i kahi pilina ma ka ʻaoʻao e loaʻa ana:
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);
}
Loaʻa i ka ʻeke mua a hoʻouna i kahi hōʻoia (FirstPacketReceived state):
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);
}
}
Hohonu i ke code. Ke pani nei i ka pilina ma ka manawa pau
He ʻāpana koʻikoʻi ka lawelawe ʻana i ka manawa pau o ka UDP Pono. E noʻonoʻo i kahi laʻana i hāʻule ʻole ai kahi node waena a ua hiki ʻole ke lawe ʻana i ka ʻikepili ma nā ʻaoʻao ʻelua. Diagram no ka pani ʻana i kahi pilina ma ka manawa pau:
E like me ka ʻike ʻia mai ke kiʻikuhi, hoʻomaka koke ka manawa hana a ka mea hoʻouna ma hope o ka hoʻouna ʻana i kahi poloka o nā ʻeke. Hana ʻia kēia ma ke ʻano SendPacket o ka mokuʻāina SendingCycle. E ho'ā ana i ka manawa hana (SendingCycle state):
public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
{
// отправляем блок пакетов
// ...
// перезапускаем таймер после отправки
connectionRecord.WaitForPacketsTimer.Change( connectionRecord.ShortTimerPeriod, -1 );
if ( connectionRecord.CloseWaitTimer != null )
connectionRecord.CloseWaitTimer.Change( -1, -1 );
}
Hoʻonohonoho ʻia nā manawa manawa i ka wā i hana ʻia ai ka pilina. ʻO ka ShortTimerPeriod paʻamau he 5 kekona. I ka laʻana, ua hoʻonohonoho ʻia i 1,5 kekona.
No kahi pilina e hiki mai ana, hoʻomaka ka manawa ma hope o ka loaʻa ʻana o ka ʻeke ʻikepili komo hope loa, hana kēia ma ke ʻano ReceivePacket o ka mokuʻāina. Hoʻohui Ke hoʻā ʻana i ka manawa hana (ʻĀpana hui):
ʻAʻole i hōʻea hou nā ʻeke i ka pilina e hiki mai ana ke kali nei i ka manawa hana. Ua hele ka manawa a kapa ʻia ke ʻano ProcessPackets, kahi i loaʻa ai nā ʻeke i nalowale a hoʻouna ʻia nā noi hoʻouna no ka manawa mua. Ke hoʻouna ʻana i nā noi hoʻouna hou ʻana (Ka mokuʻāina hoʻohui):
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);
}
}
Hoʻonohonoho ʻia ka ʻano hoʻololi TimerSecondTry i oiaio. Aia kēia ʻano hoʻololi no ka hoʻomaka hou ʻana i ka manawa hana.
Ma ka ʻaoʻao o ka mea hoʻouna, hoʻāla ʻia ka manawa hana a hoʻouna ʻia ka ʻeke hope i hoʻouna ʻia. Ke ho'ā nei i ka manawa pili pili (SendingCycle state):
public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
{
// ...
// отправляем повторно последний пакет
// ...
// включаем таймер CloseWait – для ожидания восстановления соединения или его завершения
StartCloseWaitTimer(connectionRecord);
}
Ma hope o kēlā, hoʻomaka ka manawa pani pili i ka pilina i waho. 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);
}
He 30 kekona ka manawa hoʻopau manawa pili pili ma ke ʻano paʻamau.
Ma hope o ka manawa pōkole, ke ahi hou ka manawa hana ma ka ʻaoʻao o ka mea loaʻa, hoʻouna hou ʻia nā noi, a laila hoʻomaka ka manawa pili pili no ka pilina e komo mai.
Ke ahi nā manawa pili, hoʻokuʻu ʻia nā kumuwaiwai āpau o nā moʻolelo pili ʻelua. Hōʻike ka mea hoʻouna i ka hāʻule ʻole o ka hāʻawi ʻana i ka noi o luna (ʻike i ka API UDP hilinaʻi). Ke hoʻokuʻu nei i nā kumuwaiwai moʻolelo pili:
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);
}
}
Hohonu i ke code. Hoʻihoʻi hou i ka hoʻoili ʻikepili
Kiʻikuhi hoʻihoʻi hoʻihoʻi ʻikepili i ka hihia o ka poho packet:
E like me ka mea i kūkākūkā mua ʻia ma ka pani ʻana i ka pilina ma ka manawa pau, ke pau ka manawa hana, e nānā ka mea hoʻokipa no nā ʻeke nalowale. Inā nalowale ka ʻeke, e hōʻuluʻulu ʻia kahi papa inoa o ka nui o nā ʻeke i hiki ʻole i ka mea loaʻa. Hoʻokomo ʻia kēia mau helu i loko o ka pūʻulu LostPackets o kahi pilina kikoʻī, a hoʻouna ʻia nā noi no ka hoʻouna hou ʻana. Ke hoʻouna ʻana i nā noi e hoʻouna hou i nā pūʻolo (Ka mokuʻāina ʻo 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);
}
}
// ...
}
}
E ʻae ka mea hoʻouna i ka noi hoʻouna hou a hoʻouna i nā ʻeke i nalowale. He mea pono e hoʻomaopopo i kēia manawa ua hoʻomaka ka mea hoʻouna i ka manawa pili pili a, i ka loaʻa ʻana o kahi noi, ua hoʻonohonoho hou ʻia. Hoʻouna hou i nā ʻeke i nalowale (SendingCycle state):
public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload)
{
// ...
connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
// сброс таймера закрытия соединения
if (connectionRecord.CloseWaitTimer != null)
connectionRecord.CloseWaitTimer.Change(-1, -1);
// ...
// это запрос на повторную передачу – отправляем требуемый пакет
else
ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, header.PacketNumber));
}
Loaʻa ka ʻeke hōʻino (packet #3 ma ke kiʻikuhi) e ka pilina e komo mai ana. Hana ʻia kahi ʻike e ʻike inā ua piha ka puka aniani a hoʻihoʻi ʻia ka hoʻouna ʻikepili maʻamau. Ke nānā ʻana i nā hits ma ka puka makani loaʻa (Ssembling 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 UDP hilinaʻi
No ke kamaʻilio ʻana me ka protocol transfer data, aia kahi papa Udp Reliable, kahi mea hoʻopili ma luna o ka poloka mana hoʻoili. Eia nā lālā koʻikoʻi o ka papa:
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()
}
Loaʻa nā memo ma ke kau inoa ʻana. E hāʻawi i ka pūlima no ke ʻano kelepona:
public delegate void ReliableUdpMessageCallback( ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteClient );
Memo:
public class ReliableUdpMessage
{
// тип сообщения, простое перечисление
public ReliableUdpMessageTypes Type { get; private set; }
// данные сообщения
public byte[] Body { get; private set; }
// если установлено в true – механизм подтверждения доставки будет отключен
// для передачи конкретного сообщения
public bool NoAsk { get; private set; }
}
No ke kau inoa ʻana i kahi ʻano memo kikoʻī a/a i ʻole kekahi mea hoʻouna kikoʻī, hoʻohana ʻia nā ʻāpana koho ʻelua: ReliableUdpMessageTypes messageType a me IPEndPoint ipEndPoint.
Nā ʻano memo:
public enum ReliableUdpMessageTypes : short
{
// Любое
Any = 0,
// Запрос к STUN server
StunRequest = 1,
// Ответ от STUN server
StunResponse = 2,
// Передача файла
FileTransfer =3,
// ...
}
Hoʻouna ʻia ka ʻōlelo asynchronously; no kēia, hoʻokō ka protocol i kahi ʻano hoʻolālā asynchronous:
public IAsyncResult BeginSendMessage(ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteEndPoint, AsyncCallback asyncCallback, Object state)
He ʻoiaʻiʻo ka hopena o ka hoʻouna ʻana i kahi memo - inā ua hiki ka memo i ka mea loaʻa a me ka wahaheʻe - inā ua pani ʻia ka pilina ma ka manawa pau:
public bool EndSendMessage(IAsyncResult asyncResult)
hopena
ʻAʻole i wehewehe nui ʻia ma kēia ʻatikala. ʻO nā mīkini hoʻohālikelike thread, ʻokoʻa a me ka lawelawe hewa, hoʻokō i nā ʻano hoʻouna memo asynchronous. Akā ʻo ke kumu o ka protocol, ka wehewehe ʻana o ka loiloi no ka hoʻoponopono ʻana i nā ʻeke, ka hoʻokumu ʻana i kahi pilina, a me ka mālama ʻana i nā manawa manawa, pono e maopopo iā ʻoe.
ʻO ka mana i hōʻike ʻia o ka protocol delivery hilinaʻi he paʻa a hiki ke hoʻokō i nā koi i wehewehe mua ʻia. Akā makemake wau e hoʻohui e hiki ke hoʻomaikaʻi ʻia ka hoʻokō i wehewehe ʻia. No ka laʻana, e hoʻonui i ka throughput a hoʻololi i ka manawa manawa, hiki ke hoʻohui ʻia nā mīkini e like me ka puka aniani a me ka RTT i ka protocol, e pono nō hoʻi e hoʻokō i kahi mīkini no ka hoʻoholo ʻana i ka MTU ma waena o nā node pili (akā inā hoʻouna ʻia nā memo nui) .
Mahalo i kou nānā ʻana, ke kakali nei au i kāu mau manaʻo a me nā manaʻo.
PS No ka poʻe makemake i nā kikoʻī a i ʻole makemake e hoʻāʻo i ka protocol, ka loulou i ka papahana ma GitHube: Pahana UDP hilinaʻi
Hou: Mahalo mayorovp и sidristij no ka manaʻo e hoʻohui i kahi hana i ka interface. ʻAʻole i uhaki ʻia ka hoʻohālikelike o ka waihona me nā ʻōnaehana hana kahiko, no ka mea Kākoʻo ka 4th framework iā XP a me 2003 server.