/// <summary> /// Initializes a new instance of the ReliableUdpConnectionRecord /// to send message /// </summary> /// <param name="key">EndPoint and TransmissionId key</param> /// <param name="tcb">Connection control block. <see cref="ReliableUdpConnectionControlBlock"/></param> /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param> /// <param name="cToken">CancellationToken</param> /// <param name="asyncResult"><see cref="AsyncResultSendMessage"/></param> /// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="tcb"/> is a null reference</exception> public ReliableUdpConnectionRecord(Tuple <EndPoint, Int32> key, ReliableUdpConnectionControlBlock tcb, ReliableUdpMessage reliableUdpMessage, CancellationToken cToken, AsyncResultSendMessage asyncResult) : this(key, tcb) { if (reliableUdpMessage == null) { throw new ArgumentNullException("reliableUdpMessage"); } if (reliableUdpMessage.Body == null || reliableUdpMessage.Body.Length == 0) { throw new ArgumentNullException("reliableUdpMessage", "reliableUdpMessage.Body can not be null."); } CToken = cToken; this.AsyncResult = asyncResult; this.ReliableUdpMessageType = reliableUdpMessage.Type; this.IsNoAnswerNeeded = reliableUdpMessage.NoAsk; //RU: добавляем содержимое ReliableUdpMessage в словарь ReliableUdpConnectionControlBlock'a //EN: fills the array of outcoming message with message bytes this.OutcomingStream = Tcb.OutcomingStreams.GetOrAdd(key, reliableUdpMessage.Body); //RU: Расчитываем количество пакетов необходимых для отправки сообщения this.NumberOfPackets = (int)Math.Ceiling((double)((double)OutcomingStream.Length / (double)this.BufferSize)); //RU: переключаем состояние на отправку первого пакета //EN: set initial state this.State = Tcb.States.FirstPacketSending; }
private IAsyncResult BeginSendTask(WrappedBeginSendParameters obj, AsyncCallback callback, Object state) { //RU: упаковали данные в собственный AsyncResult //EN: wrapped data in our own AsyncResult AsyncResultSendMessage ar = new AsyncResultSendMessage(obj.ReliableUdpMessage, obj.RemoteEndPoint, obj.Token, callback, state, this); ThreadPool.QueueUserWorkItem(StartTransmissionHelper, ar); return(ar); }
/// <summary> /// Sends the <paramref name="reliableUdpMessage"/> asynchronously to the specified endpoint. /// </summary> /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param> /// <param name="remoteEndPoint">Ip endpoint of recipient of the message.</param> /// <param name="callback">The <see cref="AsyncCallback"/> delegate.</param> /// <param name="state">An object that contains state information for this request.</param> /// <returns>An IAsyncResult that references the asynchronous send.</returns> public IAsyncResult BeginSend(ReliableUdpMessage reliableUdpMessage, IPEndPoint remoteEndPoint, AsyncCallback callback, Object state) { //RU: упаковали данные в собственный AsyncResult //EN: wrapped data in our own AsyncResult AsyncResultSendMessage ar = new AsyncResultSendMessage(reliableUdpMessage, remoteEndPoint, CancellationToken.None, callback, state, this); ThreadPool.QueueUserWorkItem(StartTransmissionHelper, ar); return(ar); }
/// <summary> /// Creates new ReliableUdpConnectionRecord and starts finite state automaton /// </summary> /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param> /// <param name="endPoint">Ip endpoint of recipient of the message.</param> /// <param name="asyncResult"><see cref="AsyncResultSendMessage"/> object</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ReliableUdpConfigurationException"></exception> /// <returns></returns> private void StartTransmission(ReliableUdpMessage reliableUdpMessage, EndPoint endPoint, CancellationToken cToken, AsyncResultSendMessage asyncResult) { try { 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); } //RU: создаем ключ для словаря, на основе EndPoint и ReliableUdpHeader.TransmissionId //EN: create key based on EndPoint and ReliableUdpHeader.TransmissionId for dictionary byte[] transmissionId = new byte[4]; //RU: только криптопровайдер! random выдает одинаковые номера для двух одновременно созданных TCB //EN: should use only cryptographic random function. System.Random gives the same values for two //EN: or more ConnnectionControlBlock created at the same time m_randomCrypto.GetBytes(transmissionId); Tuple <EndPoint, Int32> key = new Tuple <EndPoint, Int32>(endPoint, BitConverter.ToInt32(transmissionId, 0)); Debug.WriteLine("Transmission Id is {0}", key.Item2); //EN: make two attemps to add key into dictionary if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, reliableUdpMessage, cToken, 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, cToken, asyncResult))) { throw new ArgumentException("Pair TransmissionId & EndPoint is already exists in the dictionary"); } } //RU: запустили состояние в обработку. //EN: run state handling m_listOfHandlers[key].State.SendPacket(m_listOfHandlers[key]); } //RU: Все исключения в состояниях обрабатываются отдельно //RU: здесь ловятся только те, которые могут появиться до запуска конечного автомата //EN: All exceptions that raised in states are handled separately catch (ArgumentNullException ex) { if (asyncResult != null) { asyncResult.SetAsCompleted(ex); } else { throw; } } catch (ArgumentOutOfRangeException ex) { if (asyncResult != null) { asyncResult.SetAsCompleted(ex); } else { throw; } } catch (ArgumentException ex) { if (asyncResult != null) { asyncResult.SetAsCompleted(ex); } else { throw; } } catch (ReliableUdpConfigurationException ex) { if (asyncResult != null) { asyncResult.SetAsCompleted(ex); } else { throw; } } }
/// <summary> /// Wrapper of StartTransmission method /// </summary> /// <param name="asyncResult">Parameter to pass through thread pool</param> private void StartTransmissionHelper(Object asyncResult) { AsyncResultSendMessage ar = (AsyncResultSendMessage)asyncResult; StartTransmission(ar.ReliableUdpMessage, ar.EndPoint, ar.Token, ar); }
/// <summary> /// Ends a pending asynchronous send. /// </summary> /// <param name="asyncResult">An IAsyncResult that stores state information for this asynchronous operation. /// </param> /// <returns><c>true</c> if the message was successfully sent. /// <c>false</c> if sending the message was interrupted on a timeout. /// if <see cref="ReliableUdpMessage.NoAsk"/> is set to <c>true</c> the message can be delivered /// damaged even if returned result equals <c>true</c></returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ReliableUdpConfigurationException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="SocketException"></exception> public bool EndSend(IAsyncResult asyncResult) { AsyncResultSendMessage ar = (AsyncResultSendMessage)asyncResult; return(AsyncResultSendMessage.EndInvoke(ar, this)); }