Exemple #1
0
        /// <summary>
        /// Assigns an incremented message id (if none is already set) and performs a blocking Send operation.
        /// <para>If the mssage is <see cref="CoapMessageType.Confirmable"/>, the client will wait for a response with a coresponding message Id for the <see cref="RetransmitTimeout"/>* * <see cref="MaxRetransmitAttempts"/></para>
        /// </summary>
        /// <param name="message">The CoAP message to send. It's <see cref="CoapMessage.Id"/> may be set if it is unassigned.</param>
        /// <param name="endpoint">The remote endpoint to send the message to.
        /// <para>The endpoint must implement the same underlying transport to succeed.</para>
        /// </param>
        /// <param name="token">A token used to cancel the blocking Send operation or retransmission attempts.</param>
        /// <returns>The message Id</returns>
        /// <exception cref="CoapClientException">If the timeout period * maximum retransmission attempts was reached.</exception>
        public virtual async Task <CoapMessageIdentifier> SendAsync(CoapMessage message, ICoapEndpoint endpoint, CancellationToken token)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (Endpoint == null)
            {
                throw new CoapEndpointException($"{nameof(CoapClient)} has an invalid {nameof(Endpoint)}");
            }

            if (message.Id == 0)
            {
                message.Id = GetNextMessageId();
            }

            if (message.IsMulticast && message.Type != CoapMessageType.NonConfirmable)
            {
                throw new CoapClientException("Can not send confirmable (CON) CoAP message to a multicast endpoint");
            }

            var messageId = message.GetIdentifier(endpoint, message.Type == CoapMessageType.Confirmable || message.Type == CoapMessageType.NonConfirmable);

            _messageResponses.TryAdd(messageId, new TaskCompletionSource <CoapMessage>(TaskCreationOptions.RunContinuationsAsynchronously));

            if (message.Type != CoapMessageType.Confirmable)
            {
                await SendAsyncInternal(message, endpoint, token).ConfigureAwait(false);

                return(messageId);
            }

            if (!_messageResponses.TryGetValue(messageId, out var responseTaskSource))
            {
                throw new CoapClientException("Race condition? This shouldn't happen. Congratuations!");
            }

            for (var attempt = 1; attempt <= MaxRetransmitAttempts; attempt++)
            {
                StartReceiveAsyncInternal();

                await SendAsyncInternal(message, endpoint, token).ConfigureAwait(false);

                token.ThrowIfCancellationRequested();

                var timeout = TimeSpan.FromMilliseconds(RetransmitTimeout.TotalMilliseconds * attempt);

                await Task.WhenAny(responseTaskSource.Task, Task.Delay(timeout, token)).ConfigureAwait(false);

                token.ThrowIfCancellationRequested();


                if (responseTaskSource.Task.IsCompleted)
                {
                    return(messageId);
                }
            }
            throw new CoapClientException($"Max retransmission attempts reached for Message Id: {message.Id}");
        }
Exemple #2
0
 /// <summary>
 /// Checks if a <see cref="CoapReceiveResult"/> has been received for the coresponding <paramref name="request"/> and returns it.
 /// Otherwise waits until a new result is received unless cancelled by the <paramref name="token"/> or the <see cref="MaxRetransmitAttempts"/> is reached.
 /// </summary>
 /// <param name="request">Waits for a result with a coresponding request message.</param>
 /// <param name="token">Token to cancel the blocking Receive operation</param>
 /// <returns>Valid result if a result is received, <c>null</c> if canceled.</returns>
 /// <exception cref="CoapClientException">If the timeout period * maximum retransmission attempts was reached.</exception>
 public Task <CoapMessage> GetResponseAsync(CoapMessage request, ICoapEndpoint endpoint = null, bool isRequest = false, CancellationToken token = default(CancellationToken), bool dequeue = true)
 => GetResponseAsync(request.GetIdentifier(endpoint, isRequest), token, dequeue);
Exemple #3
0
        private async Task ReceiveAsyncInternal()
        {
            var isMulticast = Endpoint?.IsMulticast ?? false;

            try
            {
                while (true)
                {
                    Task <CoapPacket> payloadTask;
                    lock (this)
                    {
                        if (Endpoint == null)
                        {
                            return;
                        }

                        payloadTask = Endpoint.ReceiveAsync(_receiveTaskCTS.Token);
                    }

                    var payload    = await payloadTask;
                    var receivedAt = DateTime.Now;

                    var message = new CoapMessage {
                        IsMulticast = isMulticast
                    };
                    try
                    {
                        message.FromBytes(payload.Payload);

                        // Ignore non-empty reset messages
                        if (message.Type == CoapMessageType.Reset && message.Code != CoapMessageCode.None)
                        {
                            continue;
                        }

                        // Reject confirmable empty messages
                        if (message.Type == CoapMessageType.Confirmable && message.Code == CoapMessageCode.None)
                        {
                            await SendAsync(new CoapMessage { Id = message.Id, Type = CoapMessageType.Reset }, payload.Endpoint);

                            continue;
                        }

                        // Ignore repeated messages
                        if (IsRepeated(payload.Endpoint, message.Id))
                        {
                            continue;
                        }
                    }
                    catch (CoapMessageFormatException)
                    {
                        if (message.Type == CoapMessageType.Confirmable &&
                            !isMulticast)
                        {
                            await SendAsync(new CoapMessage
                            {
                                Id   = message.Id,
                                Type = CoapMessageType.Reset
                            }, payload.Endpoint);
                        }
                        if (message.Type == CoapMessageType.Acknowledgement &&
                            Coap.ReservedMessageCodeClasses.Contains(message.Code.Class))
                        {
                            continue;
                        }

                        throw;
                    }

                    lock (_recentMessages)
                    {
                        var messageId = message.GetIdentifier(payload.Endpoint);

                        if (_messageResponses.ContainsKey(messageId))
                        {
                            _messageResponses[messageId].TrySetResult(message);
                        }

                        _recentMessages.Enqueue(Tuple.Create(receivedAt, payload.Endpoint, message));
                    }

                    _receiveQueue.Enqueue(Task.FromResult(new CoapReceiveResult(payload.Endpoint, message)));
                    _receiveEvent.Set();
                }
            }
            catch (Exception ex)
            {
                if (ex is CoapEndpointException)
                {
                    var endpoint = Endpoint;

                    lock (this)
                        Endpoint = null;

                    endpoint?.Dispose();

                    foreach (var response in _messageResponses.Values)
                    {
                        response.TrySetCanceled();
                    }
                }

                // Gona cheat and enque that exception so it gets thrown as if this detached-infinite-loop never existed...
                _receiveQueue.Enqueue(Task.FromException <CoapReceiveResult>(ex));

                _receiveEvent.Set();
            }
        }