private void ReceiveData(DataReceivedEventArgs e) { IMessageDecoder decoder = Spec.NewMessageDecoder(e.Data); if (decoder.IsRequest) { Request request; try { request = decoder.DecodeRequest(); } catch (Exception) { if (decoder.IsReply) { if (log.IsWarnEnabled) { log.Warn("Message format error caused by " + e.EndPoint); } } else { // manually build RST from raw information EmptyMessage rst = new EmptyMessage(MessageType.RST); rst.Destination = e.EndPoint; rst.ID = decoder.ID; _channel.Send(Serialize(rst), rst.Destination); if (log.IsWarnEnabled) { log.Warn("Message format error caused by " + e.EndPoint + " and reseted."); } } return; } request.Source = e.EndPoint; Exchange exchange = _matcher.ReceiveRequest(request); if (exchange != null) { exchange.EndPoint = this; _coapStack.ReceiveRequest(exchange, request); } } else if (decoder.IsResponse) { Response response = decoder.DecodeResponse(); response.Source = e.EndPoint; Exchange exchange = _matcher.ReceiveResponse(response); if (exchange != null) { response.RTT = (DateTime.Now - exchange.Timestamp).TotalMilliseconds; exchange.EndPoint = this; _coapStack.ReceiveResponse(exchange, response); } else if (response.Type != MessageType.ACK) { if (log.IsDebugEnabled) { log.Debug("Rejecting unmatchable response from " + e.EndPoint); } Reject(response); } } else if (decoder.IsEmpty) { EmptyMessage message = decoder.DecodeEmptyMessage(); message.Source = e.EndPoint; // CoAP Ping if (message.Type == MessageType.CON || message.Type == MessageType.NON) { if (log.IsDebugEnabled) { log.Debug("Responding to ping by " + e.EndPoint); } Reject(message); } else { Exchange exchange = _matcher.ReceiveEmptyMessage(message); if (exchange != null) { exchange.EndPoint = this; _coapStack.ReceiveEmptyMessage(exchange, message); } } } else if (log.IsDebugEnabled) { log.Debug("Silently ignoring non-CoAP message from " + e.EndPoint); } }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { _executor.Start(() => _coapStack.SendResponse(exchange, response)); }
/// <inheritdoc/> public void SendEmptyMessage(Exchange exchange, EmptyMessage message) { _executor.Start(() => _coapStack.SendEmptyMessage(exchange, message)); }
/// <inheritdoc/> public Exchange ReceiveResponse(Response response) { /* * This response could be * - The first CON/NON/ACK+response => deliver * - Retransmitted CON (because client got no ACK) * => resend ACK */ Exchange.KeyID keyId = new Exchange.KeyID(response.ID, response.Source); Exchange.KeyToken keyToken = new Exchange.KeyToken(response.Token, response.Source); Exchange exchange; if (_exchangesByToken.TryGetValue(keyToken, out exchange)) { // There is an exchange with the given token Exchange prev = _deduplicator.FindPrevious(keyId, exchange); if (prev != null) { // (and thus it holds: prev == exchange) if (log.IsDebugEnabled) { log.Debug("Duplicate response " + response); } response.Duplicate = true; } else { if (log.IsDebugEnabled) { log.Debug("Exchange got reply: Cleaning up " + keyId); } _exchangesByID.Remove(keyId); } if (response.Type == MessageType.ACK && exchange.CurrentRequest.ID != response.ID) { // The token matches but not the MID. This is a response for an older exchange if (log.IsWarnEnabled) { log.Warn("Token matches but not MID: Expected " + exchange.CurrentRequest.ID + " but was " + response.ID); } // ignore response return(null); } else { // this is a separate response that we can deliver return(exchange); } } else { // There is no exchange with the given token. if (response.Type != MessageType.ACK) { Exchange prev = _deduplicator.Find(keyId); if (prev != null) { response.Duplicate = true; return(prev); } } // ignore response return(null); } }
/// <inheritdoc/> public Exchange ReceiveRequest(Request request) { /* * This request could be * - Complete origin request => deliver with new exchange * - One origin block => deliver with ongoing exchange * - Complete duplicate request or one duplicate block (because client got no ACK) * => * if ACK got lost => resend ACK * if ACK+response got lost => resend ACK+response * if nothing has been sent yet => do nothing * (Retransmission is supposed to be done by the retransm. layer) */ Exchange.KeyID keyId = new Exchange.KeyID(request.ID, request.Source); /* * The differentiation between the case where there is a Block1 or * Block2 option and the case where there is none has the advantage that * all exchanges that do not need blockwise transfer have simpler and * faster code than exchanges with blockwise transfer. */ if (!request.HasOption(OptionType.Block1) && !request.HasOption(OptionType.Block2)) { Exchange exchange = new Exchange(request, Origin.Remote); Exchange previous = _deduplicator.FindPrevious(keyId, exchange); if (previous == null) { exchange.Completed += OnExchangeCompleted; return(exchange); } else { if (log.IsInfoEnabled) { log.Info("Message is a duplicate, ignore: " + request); } request.Duplicate = true; return(previous); } } else { Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, request.Source); if (log.IsDebugEnabled) { log.Debug("Lookup ongoing exchange for " + keyUri); } Exchange ongoing; if (_ongoingExchanges.TryGetValue(keyUri, out ongoing)) { Exchange prev = _deduplicator.FindPrevious(keyId, ongoing); if (prev != null) { if (log.IsInfoEnabled) { log.Info("Message is a duplicate: " + request); } request.Duplicate = true; } return(ongoing); } else { // We have no ongoing exchange for that request block. /* * Note the difficulty of the following code: The first message * of a blockwise transfer might arrive twice due to a * retransmission. The new Exchange must be inserted in both the * hash map 'ongoing' and the deduplicator. They must agree on * which exchange they store! */ Exchange exchange = new Exchange(request, Origin.Remote); Exchange previous = _deduplicator.FindPrevious(keyId, exchange); if (log.IsDebugEnabled) { log.Debug("New ongoing exchange for remote Block1 request with key " + keyUri); } if (previous == null) { exchange.Completed += OnExchangeCompleted; _ongoingExchanges[keyUri] = exchange; return(exchange); } else { if (log.IsInfoEnabled) { log.Info("Message is a duplicate: " + request); } request.Duplicate = true; return(previous); } } // if ongoing } // if blockwise }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) { response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); } /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK/RST // we only care to stop retransmission * - NON => RST // we don't care * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ if (response.Destination == null) { throw new InvalidOperationException("Response has no destination set"); } // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } if (response.HasOption(OptionType.Block2)) { Request request = exchange.Request; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 started, storing " + keyUri + "\nOngoing " + request + "\nOngoing " + response); } _ongoingExchanges[keyUri] = exchange; } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + "\nOngoing " + request + "\nOngoing " + response); } _ongoingExchanges.Remove(keyUri); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, response.Destination); _exchangesByID[keyID] = exchange; } if (response.Type == MessageType.ACK || response.Type == MessageType.NON) { // Since this is an ACK or NON, the exchange is over with sending this response. if (response.Last) { exchange.Complete = true; } } // else this is a CON and we need to wait for the ACK or RST }
/// <inheritdoc/> public Exchange ReceiveResponse(Response response) { /* * This response could be * - The first CON/NON/ACK+response => deliver * - Retransmitted CON (because client got no ACK) * => resend ACK */ Exchange.KeyID keyId; if (response.Type == MessageType.ACK) { // own namespace keyId = new Exchange.KeyID(response.ID, null); } else { // remote namespace keyId = new Exchange.KeyID(response.ID, response.Source); } Exchange.KeyToken keyToken = new Exchange.KeyToken(response.Token); Exchange exchange; if (_exchangesByToken.TryGetValue(keyToken, out exchange)) { // There is an exchange with the given token Exchange prev = _deduplicator.FindPrevious(keyId, exchange); if (prev != null) { // (and thus it holds: prev == exchange) if (log.IsInfoEnabled) { log.Info("Duplicate response for open exchange: " + response); } response.Duplicate = true; } else { keyId = new Exchange.KeyID(exchange.CurrentRequest.ID, null); if (log.IsDebugEnabled) { log.Debug("Exchange got response: Cleaning up " + keyId); } _exchangesByID.Remove(keyId); } if (response.Type == MessageType.ACK && exchange.CurrentRequest.ID != response.ID) { // The token matches but not the MID. This is a response for an older exchange if (log.IsWarnEnabled) { log.Warn("Possible MID reuse before lifetime end: " + response.TokenString + " expected MID " + exchange.CurrentRequest.ID + " but received " + response.ID); } } return(exchange); } else { // There is no exchange with the given token. if (response.Type != MessageType.ACK) { // only act upon separate responses Exchange prev = _deduplicator.Find(keyId); if (prev != null) { if (log.IsInfoEnabled) { log.Info("Duplicate response for completed exchange: " + response); } response.Duplicate = true; return(prev); } } else { if (log.IsInfoEnabled) { log.Info("Ignoring unmatchable piggy-backed response from " + response.Source + ": " + response); } } // ignore response return(null); } }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) { response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); } /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK / RST // we only care to stop retransmission * - NON => RST // we only care for observe * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } // Blockwise transfers are identified by URI and remote endpoint if (response.HasOption(OptionType.Block2)) { Request request = exchange.CurrentRequest; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); // Observe notifications only send the first block, hence do not store them as ongoing if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (Utils.Put(_ongoingExchanges, keyUri, exchange) == null) { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 started late, storing " + keyUri + " for " + request); } } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 continued, storing " + keyUri + " for " + request); } } } else { if (log.IsDebugEnabled) { log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + " for " + request); } Exchange exc; _ongoingExchanges.TryRemove(keyUri, out exc); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, null); _exchangesByID[keyID] = exchange; } // Only CONs and Observe keep the exchange active if (response.Type != MessageType.CON && response.Last) { exchange.Complete = true; } }