/**
   * When we receive a duplicate of a request, we stop it here and do not forward it to the upper
   * layer. If the server has already sent a response, we send it again. If the request has only
   * been acknowledged (but the ACK has gone lost or not reached the client yet), we resent the ACK.
   * If the request has neither been responded, acknowledged or rejected yet, the server has not yet
   * decided what to do with the request and we cannot do anything.
   */
  @Override
  public void receiveRequest(Exchange exchange, Request request) {

    if (request.isDuplicate()) {
      // Request is a duplicate, so resend ACK, RST or response
      if (exchange.getCurrentResponse() != null) {
        LOGGER.fine("Respond with the current response to the duplicate request");
        // Do not restart retransmission cycle
        super.sendResponse(exchange, exchange.getCurrentResponse());

      } else if (exchange.getCurrentRequest().isAcknowledged()) {
        LOGGER.fine(
            "The duplicate request was acknowledged but no response computed yet. Retransmit ACK");
        EmptyMessage ack = EmptyMessage.newACK(request);
        sendEmptyMessage(exchange, ack);

      } else if (exchange.getCurrentRequest().isRejected()) {
        LOGGER.fine("The duplicate request was rejected. Reject again");
        EmptyMessage rst = EmptyMessage.newRST(request);
        sendEmptyMessage(exchange, rst);

      } else {
        LOGGER.fine(
            "The server has not yet decided what to do with the request. We ignore the duplicate.");
        // The server has not yet decided, whether to acknowledge or
        // reject the request. We know for sure that the server has
        // received the request though and can drop this duplicate here.
      }

    } else {
      // Request is not a duplicate
      exchange.setCurrentRequest(request);
      super.receiveRequest(exchange, request);
    }
  }
  /**
   * If we receive an ACK or RST, we mark the outgoing request or response as acknowledged or
   * rejected respectively and cancel its retransmission.
   */
  @Override
  public void receiveEmptyMessage(Exchange exchange, EmptyMessage message) {
    exchange.setFailedTransmissionCount(0);
    // TODO: If this is an observe relation, the current response might not
    // be the one that is being acknowledged. The current response might
    // already be the next NON notification.

    if (message.getType() == Type.ACK) {
      if (exchange.getOrigin() == Origin.LOCAL) {
        exchange.getCurrentRequest().setAcknowledged(true);
      } else {
        exchange.getCurrentResponse().setAcknowledged(true);
      }
    } else if (message.getType() == Type.RST) {
      if (exchange.getOrigin() == Origin.LOCAL) {
        exchange.getCurrentRequest().setRejected(true);
      } else {
        exchange.getCurrentResponse().setRejected(true);
      }
    } else {
      LOGGER.warning("Empty messgae was not ACK nor RST: " + message);
    }

    LOGGER.finer("Cancel retransmission");
    exchange.setRetransmissionHandle(null);

    super.receiveEmptyMessage(exchange, message);
  }
  /**
   * Computes the back-off timer and schedules the specified retransmission task.
   *
   * @param exchange the exchange
   * @param task the retransmission task
   */
  protected void prepareRetransmission(Exchange exchange, RetransmissionTask task) {
    /*
     * For a new confirmable message, the initial timeout is set to a
     * random number between ACK_TIMEOUT and (ACK_TIMEOUT *
     * ACK_RANDOM_FACTOR)
     */
    int timeout;
    if (exchange.getFailedTransmissionCount() == 0) {
      timeout = getRandomTimeout(ack_timeout, (int) (ack_timeout * ack_random_factor));
    } else {
      timeout = (int) (ack_timeout_scale * exchange.getCurrentTimeout());
    }
    exchange.setCurrentTimeout(timeout);

    ScheduledFuture<?> f = executor.schedule(task, timeout, TimeUnit.MILLISECONDS);
    exchange.setRetransmissionHandle(f);
  }
    @Override
    public void run() {
      /*
       * Do not retransmit a message if it has been acknowledged,
       * rejected, canceled or already been retransmitted for the maximum
       * number of times.
       */
      try {
        int failedCount = exchange.getFailedTransmissionCount() + 1;
        exchange.setFailedTransmissionCount(failedCount);

        if (message.isAcknowledged()) {
          LOGGER.finest(
              "Timeout: message already acknowledged, cancel retransmission of " + message);
          return;

        } else if (message.isRejected()) {
          LOGGER.finest("Timeout: message already rejected, cancel retransmission of " + message);
          return;

        } else if (message.isCanceled()) {
          LOGGER.finest("Timeout: canceled (MID=" + message.getMID() + "), do not retransmit");
          return;

        } else if (failedCount <= max_retransmit) {
          LOGGER.finer(
              "Timeout: retransmit message, failed: " + failedCount + ", message: " + message);

          // Trigger MessageObservers
          message.retransmitting();

          // MessageObserver might have canceled
          if (!message.isCanceled()) retransmit();

        } else {
          LOGGER.info(
              "Timeout: retransmission limit reached, exchange failed, message: " + message);
          exchange.setTimedOut();
          message.setTimedOut(true);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
 @Override
 public void forwardRequest(Exchange exchange) {
   LOGGER.fine(
       "(DirectProxyCoapResolver 把请求交给资源 ForwardResource) Forward CoAP request to ProxyCoap2Coap: \n\t"
           + exchange.getRequest());
   /*
    * 这里的 exchange 没有包含响应结果吧?
    */
   proxyCoapClientResource.handleRequest(exchange);
 }
  /**
   * When we receive a Confirmable response, we acknowledge it and it also counts as acknowledgment
   * for the request. If the response is a duplicate, we stop it here and do not forward it to the
   * upper layer.
   */
  @Override
  public void receiveResponse(Exchange exchange, Response response) {
    exchange.setFailedTransmissionCount(0);

    exchange.getCurrentRequest().setAcknowledged(true);
    LOGGER.finest("Cancel any retransmission");
    exchange.setRetransmissionHandle(null);

    if (response.getType() == Type.CON && !exchange.getRequest().isCanceled()) {
      LOGGER.finer("Response is confirmable, send ACK");
      EmptyMessage ack = EmptyMessage.newACK(response);
      sendEmptyMessage(exchange, ack);
    }

    if (response.isDuplicate()) {
      LOGGER.fine("Response is duplicate, ignore it");
    } else {
      super.receiveResponse(exchange, response);
    }
  }
 /**
  * Handles any request in the given exchange. By default it responds with a 4.05 (Method Not
  * Allowed). Override this method if your resource handler requires advanced access to the
  * internal Exchange class. Most developer should be better off with overriding the called methods
  * {@link #handleGET(CoapExchange)}, {@link #handlePOST(CoapExchange)}, {@link
  * #handlePUT(CoapExchange)}, and {@link #handleDELETE(CoapExchange)}, which provide a better API
  * through the {@link CoapExchange} class.
  *
  * @param exchange the exchange with the request
  */
 @Override
 public void handleRequest(final Exchange exchange) {
   Code code = exchange.getRequest().getCode();
   switch (code) {
     case GET:
       handleGET(new CoapExchange(exchange, this));
       break;
     case POST:
       handlePOST(new CoapExchange(exchange, this));
       break;
     case PUT:
       handlePUT(new CoapExchange(exchange, this));
       break;
     case DELETE:
       handleDELETE(new CoapExchange(exchange, this));
       break;
   }
 }
  /** Schedules a retransmission for confirmable messages. */
  @Override
  public void sendRequest(final Exchange exchange, final Request request) {

    LOGGER.finer("Send request, failed transmissions: " + exchange.getFailedTransmissionCount());

    if (request.getType() == null) request.setType(Type.CON);

    if (request.getType() == Type.CON) {
      prepareRetransmission(
          exchange,
          new RetransmissionTask(exchange, request) {
            public void retransmit() {
              sendRequest(exchange, request);
            }
          });
    }
    super.sendRequest(exchange, request);
  }
  /**
   * Makes sure that the response type is correct. The response type for a NON can be NON or CON.
   * The response type for a CON should either be an ACK with a piggy-backed response or, if an
   * empty ACK has already be sent, a CON or NON with a separate response.
   */
  @Override
  public void sendResponse(final Exchange exchange, final Response response) {

    LOGGER.finer("Send response, failed transmissions: " + exchange.getFailedTransmissionCount());

    // If a response type is set, we do not mess around with it.
    // Only if none is set, we have to decide for one here.

    Type respType = response.getType();
    if (respType == null) {
      Type reqType = exchange.getCurrentRequest().getType();
      if (reqType == Type.CON) {
        if (exchange.getCurrentRequest().isAcknowledged()) {
          // send separate response
          response.setType(Type.CON);
        } else {
          exchange.getCurrentRequest().setAcknowledged(true);
          // send piggy-backed response
          response.setType(Type.ACK);
          response.setMID(exchange.getCurrentRequest().getMID());
        }
      } else {
        // send NON response
        response.setType(Type.NON);
      }

      LOGGER.finest(
          "Switched response message type from "
              + respType
              + " to "
              + response.getType()
              + " (request was "
              + reqType
              + ")");

    } else if (respType == Type.ACK || respType == Type.RST) {
      response.setMID(exchange.getCurrentRequest().getMID());
    }

    if (response.getType() == Type.CON) {
      LOGGER.finer("Scheduling retransmission for " + response);
      prepareRetransmission(
          exchange,
          new RetransmissionTask(exchange, response) {
            public void retransmit() {
              sendResponse(exchange, response);
            }
          });
    }
    super.sendResponse(exchange, response);
  }
示例#10
0
  /**
   * This method is used to apply resource-specific knowledge on the exchange. If the request was
   * successful, it sets the Observe option for the response. It is important to use the
   * notificationOrderer of the resource here. Further down the layer, race conditions could cause
   * local reordering of notifications. If the response has an error code, no observe relation can
   * be established and if there was one previously it is canceled. When this resource allows to be
   * observed by clients and the request is a GET request with an observe option, the {@link
   * ServerMessageDeliverer} already created the relation, as it manages the observing endpoints
   * globally.
   *
   * @param exchange the exchange
   * @param response the response
   */
  public void checkObserveRelation(Exchange exchange, Response response) {
    /*
     * If the request for the specified exchange tries to establish an observer
     * relation, then the ServerMessageDeliverer must have created such a relation
     * and added to the exchange. Otherwise, there is no such relation.
     * Remember that different paths might lead to this resource.
     */

    ObserveRelation relation = exchange.getRelation();
    if (relation == null) return; // because request did not try to establish a relation

    if (CoAP.ResponseCode.isSuccess(response.getCode())) {
      response.getOptions().setObserve(notificationOrderer.getCurrent());

      if (!relation.isEstablished()) {
        relation.setEstablished(true);
        addObserveRelation(relation);
      } else if (observeType != null) {
        // The resource can control the message type of the notification
        response.setType(observeType);
      }
    } // ObserveLayer takes care of the else case
  }