/*
  * Listen on the specified address and port. Return a listener
  * that encapsulates the ServerSocket.
  */
 ListenKey startListening(String localaddress, int port) throws IOException {
   InetSocketAddress sa;
   if (localaddress == null) {
     sa = new InetSocketAddress(port);
   } else {
     sa = new InetSocketAddress(localaddress, port);
   }
   ServerSocket ss = new ServerSocket();
   ss.bind(sa);
   return new SocketListenKey(ss);
 }
  /** Stop the listener */
  public void stopListening(ListenKey listener) throws IOException {
    if (!(listener instanceof SocketListenKey)) {
      throw new IllegalArgumentException("Invalid listener");
    }

    synchronized (listener) {
      ServerSocket ss = ((SocketListenKey) listener).socket();

      // if the ServerSocket has been closed it means
      // the listener is invalid
      if (ss.isClosed()) {
        throw new IllegalArgumentException("Invalid listener");
      }
      ss.close();
    }
  }
    /*
     * Returns the string representation of the address that this
     * listen key represents.
     */
    public String address() {
      InetAddress address = ss.getInetAddress();

      /*
       * If bound to the wildcard address then use current local
       * hostname. In the event that we don't know our own hostname
       * then assume that host supports IPv4 and return something to
       * represent the loopback address.
       */
      if (address.isAnyLocalAddress()) {
        try {
          address = InetAddress.getLocalHost();
        } catch (UnknownHostException uhe) {
          byte[] loopback = {0x7f, 0x00, 0x00, 0x01};
          try {
            address = InetAddress.getByAddress("127.0.0.1", loopback);
          } catch (UnknownHostException x) {
            throw new InternalError("unable to get local hostname");
          }
        }
      }

      /*
       * Now decide if we return a hostname or IP address. Where possible
       * return a hostname but in the case that we are bound to an
       * address that isn't registered in the name service then we
       * return an address.
       */
      String result;
      String hostname = address.getHostName();
      String hostaddr = address.getHostAddress();
      if (hostname.equals(hostaddr)) {
        if (address instanceof Inet6Address) {
          result = "[" + hostaddr + "]";
        } else {
          result = hostaddr;
        }
      } else {
        result = hostname;
      }

      /*
       * Finally return "hostname:port", "ipv4-address:port" or
       * "[ipv6-address]:port".
       */
      return result + ":" + ss.getLocalPort();
    }
  /** Accept a connection from a debuggee and handshake with it. */
  public Connection accept(ListenKey listener, long acceptTimeout, long handshakeTimeout)
      throws IOException {
    if (acceptTimeout < 0 || handshakeTimeout < 0) {
      throw new IllegalArgumentException("timeout is negative");
    }
    if (!(listener instanceof SocketListenKey)) {
      throw new IllegalArgumentException("Invalid listener");
    }
    ServerSocket ss;

    // obtain the ServerSocket from the listener - if the
    // socket is closed it means the listener is invalid
    synchronized (listener) {
      ss = ((SocketListenKey) listener).socket();
      if (ss.isClosed()) {
        throw new IllegalArgumentException("Invalid listener");
      }
    }

    // from here onwards it's possible that the ServerSocket
    // may be closed by a call to stopListening - that's okay
    // because the ServerSocket methods will throw an
    // IOException indicating the socket is closed.
    //
    // Additionally, it's possible that another thread calls accept
    // with a different accept timeout - that creates a same race
    // condition between setting the timeout and calling accept.
    // As it is such an unlikely scenario (requires both threads
    // to be using the same listener we've chosen to ignore the issue).

    ss.setSoTimeout((int) acceptTimeout);
    Socket s;
    try {
      s = ss.accept();
    } catch (SocketTimeoutException x) {
      throw new TransportTimeoutException("timeout waiting for connection");
    }

    // handshake here
    handshake(s, handshakeTimeout);

    return new SocketConnection(s);
  }