/** * If the message to be written is a {@link CoapResponse} this method decides whether the message * type is {@link MessageType.Name#ACK} (if there wasn't an empty acknowledgement sent yet) or * {@link MessageType.Name#CON} (if there was already an empty acknowledgement sent). In the * latter case it additionally cancels the sending of an empty acknowledgement (which was * scheduled by the <code>messageReceived</code> method when the request was received). * * @param ctx The {@link ChannelHandlerContext} connecting relating this class (which implements * the {@link ChannelUpstreamHandler} interface) to the datagramChannel that received the * message. * @param me the {@link MessageEvent} containing the actual message * @throws Exception if an error occurred */ @Override public void writeRequested(ChannelHandlerContext ctx, MessageEvent me) throws Exception { if (isShutdown()) return; if (me.getMessage() instanceof CoapResponse) handleOutgoingCoapResponse(ctx, me); else if (me.getMessage() instanceof InternalApplicationShutdownMessage) handleApplicationShutdown(ctx, me); else ctx.sendDownstream(me); }
private void handleIncomingConfirmableCoapResponse(ChannelHandlerContext ctx, MessageEvent me) { InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress(); CoapResponse coapResponse = (CoapResponse) me.getMessage(); writeEmptyAcknowledgement(remoteEndpoint, coapResponse.getMessageID()); ctx.sendUpstream(me); }
/** * If the incoming message is a confirmable {@link CoapRequest} it schedules the sending of an * empty acknowledgement to the sender if there wasn't a piggy-backed response within a period of * 2 seconds. * * <p>If the incoming message is a confirmable {@link CoapResponse} it immediately sends a proper * acknowledgement if there was an open request waiting for a seperate response or * update-notification. It immediately sends an RST if there was no such response expected. * * @param ctx The {@link ChannelHandlerContext} relating this handler (which implements the {@link * ChannelUpstreamHandler} interface) to the datagramChannel that received the message. * @param me the {@link MessageEvent} containing the actual message * @throws Exception if an error occured */ @Override public void messageReceived(final ChannelHandlerContext ctx, MessageEvent me) throws Exception { log.debug("Upstream from {}: {}.", me.getRemoteAddress(), me.getMessage()); if (isShutdown()) return; if (me.getMessage() instanceof CoapMessage) { CoapMessage coapMessage = (CoapMessage) me.getMessage(); if (coapMessage.getMessageTypeName() == MessageType.Name.CON) handleIncomingConfirmableCoapMessage(ctx, me); else if (coapMessage.getMessageTypeName() == MessageType.Name.NON) handleIncomingNonConfirmableMessage(ctx, me); else ctx.sendUpstream(me); } else ctx.sendUpstream(me); }
private void handleIncomingConfirmableCoapRequest(ChannelHandlerContext ctx, MessageEvent me) { InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress(); CoapMessage coapMessage = (CoapMessage) me.getMessage(); IncomingReliableMessageExchange newMessageExchange = new IncomingReliableMessageExchange(remoteEndpoint, coapMessage.getMessageID()); IncomingMessageExchange oldMessageExchange = ongoingMessageExchanges.get(remoteEndpoint, coapMessage.getMessageID()); // Check if there is an ongoing if (oldMessageExchange != null) { if (oldMessageExchange instanceof IncomingReliableMessageExchange) { // if the old message exchange is reliable and the empty ACK was already sent send another // empty ACK if (((IncomingReliableMessageExchange) oldMessageExchange).isAcknowledgementSent()) writeEmptyAcknowledgement(remoteEndpoint, coapMessage.getMessageID()); } // if the old message was unreliable and the duplicate message is confirmable send empty ACK else writeEmptyAcknowledgement(remoteEndpoint, coapMessage.getMessageID()); // As the message is already being processed there is nothing more to do return; } // try to add new reliable message exchange boolean added = false; synchronized (monitor) { Long time = System.currentTimeMillis() + MIN_EMPTY_ACK_DELAY_MILLIS; // Add message exchange to set of ongoing exchanges to detect duplicates if (!ongoingMessageExchanges.contains(remoteEndpoint, coapMessage.getMessageID())) { ongoingMessageExchanges.put(remoteEndpoint, coapMessage.getMessageID(), newMessageExchange); added = true; } // If the scheduling of the empty ACK does not work then it was already scheduled if (!emptyAcknowledgementSchedule.put(time, newMessageExchange)) { log.error("Could not schedule empty ACK for message: {}", coapMessage); ongoingMessageExchanges.remove(remoteEndpoint, coapMessage.getMessageID()); added = false; } } // everything is fine, so further process message if (added) ctx.sendUpstream(me); }
private void handleIncomingConfirmableCoapMessage(ChannelHandlerContext ctx, MessageEvent me) { InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress(); CoapMessage coapMessage = (CoapMessage) me.getMessage(); // Empty CON messages can be used as application layer PING (is CoAP endpoints alive?) if (coapMessage.getMessageCodeName() == MessageCode.Name.EMPTY) writeReset(remoteEndpoint, coapMessage.getMessageID()); else if (MessageCode.isResponse(coapMessage.getMessageCode())) handleIncomingConfirmableCoapResponse(ctx, me); else if (MessageCode.isRequest(coapMessage.getMessageCode())) handleIncomingConfirmableCoapRequest(ctx, me); else log.error("Incoming CoAP message is neither empty nor request nor response: ", coapMessage); }
private void handleOutgoingCoapResponse(ChannelHandlerContext ctx, MessageEvent me) { CoapResponse coapResponse = (CoapResponse) me.getMessage(); InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress(); IncomingMessageExchange messageExchange; synchronized (monitor) { messageExchange = ongoingMessageExchanges.remove(remoteEndpoint, coapResponse.getMessageID()); } if (messageExchange instanceof IncomingReliableMessageExchange) { // if the ongoing message exchange is reliable and the empty ACK was not yet sent make // response piggy- // backed and suppress scheduled empty ACK if (!((IncomingReliableMessageExchange) messageExchange).isAcknowledgementSent()) { coapResponse.setMessageType(MessageType.Name.ACK.getNumber()); ((IncomingReliableMessageExchange) messageExchange).setAcknowledgementSent(); } } ctx.sendDownstream(me); }
private void handleIncomingNonConfirmableMessage(ChannelHandlerContext ctx, MessageEvent me) { InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress(); CoapMessage coapMessage = (CoapMessage) me.getMessage(); boolean isDuplicate = true; if (!ongoingMessageExchanges.contains(remoteEndpoint, coapMessage.getMessageID())) { IncomingMessageExchange messageExchange = new IncomingMessageExchange(remoteEndpoint, coapMessage.getMessageID()); synchronized (monitor) { if (!ongoingMessageExchanges.contains(remoteEndpoint, coapMessage.getMessageID())) { ongoingMessageExchanges.put(remoteEndpoint, coapMessage.getMessageID(), messageExchange); isDuplicate = false; } } ctx.sendUpstream(me); } if (isDuplicate) log.info("Received duplicate (non-confirmable). IGNORE! (Message: {})", coapMessage); }