code: protected void close(final int initiator, final String reason, final LogLevel level, final Throwable t)
  	{
  		synchronized (this) {
  			if (closing > 0)
  				return;
  			closing = 1;
  		}
  		try {
  			synchronized (lock) {
  				final byte[] buf = PacketHelper.toPacket(new DisconnectRequest(channelId, new HPAI(HPAI.IPV4_UDP,
  						useNat ? null : (InetSocketAddress) ctrlSocket.getLocalSocketAddress())));
  				send(buf, ctrlEndpt);
  				long remaining = CONNECT_REQ_TIMEOUT * 1000L;
  				final long end = System.currentTimeMillis() + remaining;
  				while (closing == 1 && remaining > 0) {
  					lock.wait(remaining);
  					remaining = end - System.currentTimeMillis();
  				}
  			}
  		}
  		catch (final InterruptedException e) {
  			Thread.currentThread().interrupt();
  		}
  		catch (IOException | RuntimeException e) {
  			// we have to also catch RTEs here, since if socket already failed
  			// before close(), getLocalSocketAddress() might throw illegal argument
  			// exception or return the wildcard address, indicating a messed up socket
  			logger.error("send disconnect failed", e);
  		}
  		cleanup(initiator, reason, level, t);
  	}
docs: Called on {@link #close()}, containing all protocol specific actions to close a connection. Before this method
  returns, {@link #cleanup} is called.
  
  @param initiator one of the constants of {@link CloseEvent}
  @param reason short text statement why close was called on this connection
  @param level log level to use for logging, adjust this to the reason of closing this connection
  @param t a throwable, to pass to the logger if the close event was caused by some error, can be <code>null</code>