/** @throws IOException */
  protected void startBinaryPacketProtocol() throws IOException {
    // Send our Kex Init
    sendKeyExchangeInit();

    SshMessage msg;

    // Perform a transport protocol message loop
    while (state.getValue() != TransportProtocolState.DISCONNECTED) {
      // Process incoming messages returning any transport protocol
      // messages to be handled here
      msg = processMessages();

      if (log.isDebugEnabled()) {
        log.debug("Received " + msg.getMessageName());
      }

      switch (msg.getMessageId()) {
        case SshMsgKexInit.SSH_MSG_KEX_INIT:
          {
            onMsgKexInit((SshMsgKexInit) msg);

            break;
          }

        case SshMsgDisconnect.SSH_MSG_DISCONNECT:
          {
            onMsgDisconnect((SshMsgDisconnect) msg);

            break;
          }

        case SshMsgIgnore.SSH_MSG_IGNORE:
          {
            onMsgIgnore((SshMsgIgnore) msg);

            break;
          }

        case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED:
          {
            onMsgUnimplemented((SshMsgUnimplemented) msg);

            break;
          }

        case SshMsgDebug.SSH_MSG_DEBUG:
          {
            onMsgDebug((SshMsgDebug) msg);

            break;
          }

        default:
          onMessageReceived(msg);
      }
    }
  }
  /**
   * @param msg
   * @param sender
   * @throws IOException
   * @throws TransportProtocolException
   */
  public synchronized void sendMessage(SshMessage msg, Object sender) throws IOException {
    // Send a message, if were in key exchange then add it to
    // the list unless of course it is a transport protocol or key
    // exchange message
    if (log.isDebugEnabled()) {
      log.info("Sending " + msg.getMessageName());
    }

    int currentState = state.getValue();

    if (sender instanceof SshKeyExchange
        || sender instanceof TransportProtocolCommon
        || (currentState == TransportProtocolState.CONNECTED)) {
      sshOut.sendMessage(msg);

      if (currentState == TransportProtocolState.CONNECTED) {
        if (sendIgnore) {
          byte[] count = new byte[1];
          ConfigurationLoader.getRND().nextBytes(count);

          byte[] rand = new byte[(count[0] & 0xFF) + 1];
          ConfigurationLoader.getRND().nextBytes(rand);

          SshMsgIgnore ignore = new SshMsgIgnore(new String(rand));

          if (log.isDebugEnabled()) {
            log.debug("Sending " + ignore.getMessageName());
          }

          sshOut.sendMessage(ignore);
        }
      }
    } else if (currentState == TransportProtocolState.PERFORMING_KEYEXCHANGE) {
      log.debug("Adding to message queue whilst in key exchange");

      synchronized (messageStack) {
        // Add this message to the end of the list
        messageStack.add(msg);
      }
    } else {
      throw new TransportProtocolException("The transport protocol is disconnected");
    }
  }
  /**
   * @return
   * @throws IOException
   */
  protected SshMessage processMessages() throws IOException {
    byte[] msgdata = null;
    SshMessage msg;
    SshMessageStore ms;

    while (state.getValue() != TransportProtocolState.DISCONNECTED) {
      long currentTime = System.currentTimeMillis();

      transferredKB = sshIn.getNumBytesTransfered() / 1024 + sshOut.getNumBytesTransfered() / 1024;

      long kbLimit = transferredKB - lastTriggeredKB;

      if (((currentTime - startTime) > kexTimeout) || (kbLimit > kexTransferLimitKB)) {
        //    ((sshIn.getNumBytesTransfered() +
        //    sshOut.getNumBytesTransfered()) > kexTransferLimit)) {
        startTime = currentTime;
        lastTriggeredKB = transferredKB;
        if (log.isDebugEnabled()) {
          log.info("rekey");
        }
        sendKeyExchangeInit();
      }

      boolean hasmsg = false;

      while (!hasmsg) {
        try {
          msgdata = sshIn.readMessage();
          hasmsg = true;
        } catch (InterruptedIOException ex /*SocketTimeoutException ex*/) {
          log.info("Possible timeout on transport inputstream");

          Iterator it = eventHandlers.iterator();
          TransportProtocolEventHandler eventHandler;

          while (it.hasNext()) {
            eventHandler = (TransportProtocolEventHandler) it.next();
            eventHandler.onSocketTimeout(this /*,
                        provider.isConnected()*/);
          }
        }
      }

      Integer messageId = SshMessage.getMessageId(msgdata);

      if (!messageStore.isRegisteredMessage(messageId)) {
        try {
          ms = getMessageStore(messageId);
          msg = ms.createMessage(msgdata);

          if (log.isDebugEnabled()) {
            log.info("Received " + msg.getMessageName());
          }

          ms.addMessage(msg);
        } catch (MessageNotRegisteredException mnre) {
          log.info("Unimplemented message received " + String.valueOf(messageId.intValue()));
          msg = new SshMsgUnimplemented(sshIn.getSequenceNo());
          sendMessage(msg, this);
        }
      } else {
        return messageStore.createMessage(msgdata);
      }
    }

    throw new IOException("The transport protocol has disconnected");
  }
  /**
   * @param filter
   * @return
   * @throws IOException
   */
  public SshMessage readMessage(int[] filter) throws IOException {
    byte[] msgdata = null;
    SshMessage msg;

    while (state.getValue() != TransportProtocolState.DISCONNECTED) {
      boolean hasmsg = false;

      while (!hasmsg) {
        msgdata = sshIn.readMessage();
        hasmsg = true;
      }

      Integer messageId = SshMessage.getMessageId(msgdata);

      // First check the filter
      for (int i = 0; i < filter.length; i++) {
        if (filter[i] == messageId.intValue()) {
          if (messageStore.isRegisteredMessage(messageId)) {
            return messageStore.createMessage(msgdata);
          } else {
            SshMessageStore ms = getMessageStore(messageId);
            msg = ms.createMessage(msgdata);

            if (log.isDebugEnabled()) {
              log.debug("Processing " + msg.getMessageName());
            }

            return msg;
          }
        }
      }

      if (messageStore.isRegisteredMessage(messageId)) {
        msg = messageStore.createMessage(msgdata);

        switch (messageId.intValue()) {
          case SshMsgDisconnect.SSH_MSG_DISCONNECT:
            {
              onMsgDisconnect((SshMsgDisconnect) msg);

              break;
            }

          case SshMsgIgnore.SSH_MSG_IGNORE:
            {
              onMsgIgnore((SshMsgIgnore) msg);

              break;
            }

          case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED:
            {
              onMsgUnimplemented((SshMsgUnimplemented) msg);

              break;
            }

          case SshMsgDebug.SSH_MSG_DEBUG:
            {
              onMsgDebug((SshMsgDebug) msg);

              break;
            }

          default: // Exception not allowed
            throw new IOException("Unexpected transport protocol message");
        }
      } else {
        throw new IOException("Unexpected message received");
      }
    }

    throw new IOException("The transport protocol disconnected");
  }