@Override
  public synchronized void close(SocketChannel socketChannel) {

    assert socketChannel.isBlocking();

    if (closed) {
      return;
    }
    closed = true;
    inputBufferVendor.destruct();
    try (final ByteBufferSharing outputSharing = outputBufferVendor.open(1, TimeUnit.MINUTES)) {
      final ByteBuffer myNetData = outputSharing.getBuffer();

      engine.closeOutbound();

      SSLEngineResult result = null;

      while (!engine.isOutboundDone()) {

        // clear the buffer to receive a CLOSE message from the SSLEngine
        myNetData.clear();

        // Get close message
        result = engine.wrap(EMPTY_BYTE_BUFFER, myNetData);

        /*
         * We would have liked to make this one of the while() conditions but
         * if status is CLOSED we'll get a "Broken pipe" exception in the next write()
         * so it needs to be handled here.
         */
        if (result.getStatus() == CLOSED) {
          break;
        }

        // Send close message to peer
        myNetData.flip();
        while (myNetData.hasRemaining()) {
          socketChannel.write(myNetData);
        }
      }
      if (result != null && result.getStatus() != CLOSED) {
        throw new SSLHandshakeException(
            "Error closing SSL session.  Status=" + result.getStatus());
      }
    } catch (ClosedChannelException e) {
      // we can't send a close message if the channel is closed
    } catch (IOException e) {
      throw new GemFireIOException("exception closing SSL session", e);
    } catch (final OpenAttemptTimedOut _unused) {
      logger.info(String.format("Couldn't get output lock in time, eliding TLS close message"));
      if (!engine.isOutboundDone()) {
        engine.closeOutbound();
      }
    } finally {
      outputBufferVendor.destruct();
    }
  }
This will throw an SSLHandshakeException if the handshake doesn't terminate in a FINISHED
state. It may throw other IOExceptions caused by I/O operations
geode-core/src/main/java/org/apache/geode/internal/net/NioSslEngine.java