private DatagramChannel connectClient() throws IOException {
    final DatagramChannel client =
        address.isMulticastAddress()
            ? DatagramChannel.open(address.getAddress().length == 4 ? INET : INET6)
            : DatagramChannel.open();

    final InetSocketAddress hostAddress = new InetSocketAddress(port);
    client.configureBlocking(false);
    if (address.isMulticastAddress()) {
      client.setOption(SO_REUSEADDR, true);
      client.bind(hostAddress);
      if (networkInterface != null) {
        // This is probably not needed, because client socket doesn't send datagrams,
        // but since EVERYBODY on the internet configures this for any channels, and
        // I don't see any harm this config could make, I leave it here
        client.setOption(IP_MULTICAST_IF, networkInterface);
        client.join(address, networkInterface);
      } else {
        client.join(address, NetworkInterface.getByInetAddress(hostAddress.getAddress()));
      }
      if (LOG.isDebugEnabled()) LOG.debug("Connecting via multicast, group=" + address);
    } else {
      client.bind(hostAddress);
    }

    if (LOG.isDebugEnabled()) LOG.debug("Listening on port " + port);
    closeables.add(client);
    return client;
  }
  @Override
  @SuppressWarnings("unchecked")
  public <T> T getOption(SocketOption<T> name) throws IOException {
    if (name == null) throw new NullPointerException();
    if (!supportedOptions().contains(name))
      throw new UnsupportedOperationException("'" + name + "' not supported");

    synchronized (stateLock) {
      ensureOpen();

      if (name == StandardSocketOptions.IP_TOS) {
        // IPv4 only; always return 0 on IPv6
        if (family == StandardProtocolFamily.INET) {
          return (T) Net.getSocketOption(fd, family, name);
        } else {
          return (T) Integer.valueOf(0);
        }
      }

      if (name == StandardSocketOptions.IP_MULTICAST_TTL
          || name == StandardSocketOptions.IP_MULTICAST_LOOP) {
        return (T) Net.getSocketOption(fd, family, name);
      }

      if (name == StandardSocketOptions.IP_MULTICAST_IF) {
        if (family == StandardProtocolFamily.INET) {
          int address = Net.getInterface4(fd);
          if (address == 0) return null; // default interface

          InetAddress ia = Net.inet4FromInt(address);
          NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
          if (ni == null) throw new IOException("Unable to map address to interface");
          return (T) ni;
        } else {
          int index = Net.getInterface6(fd);
          if (index == 0) return null; // default interface

          NetworkInterface ni = NetworkInterface.getByIndex(index);
          if (ni == null) throw new IOException("Unable to map index to interface");
          return (T) ni;
        }
      }

      // no special handling
      return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
    }
  }
  @Override
  public <T> DatagramChannel setOption(SocketOption<T> name, T value) throws IOException {
    if (name == null) throw new NullPointerException();
    if (!supportedOptions().contains(name))
      throw new UnsupportedOperationException("'" + name + "' not supported");

    synchronized (stateLock) {
      ensureOpen();

      if (name == StandardSocketOptions.IP_TOS) {
        // IPv4 only; no-op for IPv6
        if (family == StandardProtocolFamily.INET) {
          Net.setSocketOption(fd, family, name, value);
        }
        return this;
      }

      if (name == StandardSocketOptions.IP_MULTICAST_TTL
          || name == StandardSocketOptions.IP_MULTICAST_LOOP) {
        // options are protocol dependent
        Net.setSocketOption(fd, family, name, value);
        return this;
      }

      if (name == StandardSocketOptions.IP_MULTICAST_IF) {
        if (value == null)
          throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
        NetworkInterface interf = (NetworkInterface) value;
        if (family == StandardProtocolFamily.INET6) {
          int index = interf.getIndex();
          if (index == -1) throw new IOException("Network interface cannot be identified");
          Net.setInterface6(fd, index);
        } else {
          // need IPv4 address to identify interface
          Inet4Address target = Net.anyInet4Address(interf);
          if (target == null) throw new IOException("Network interface not configured for IPv4");
          int targetAddress = Net.inet4AsInt(target);
          Net.setInterface4(fd, targetAddress);
        }
        return this;
      }

      // remaining options don't need any special handling
      Net.setSocketOption(fd, Net.UNSPEC, name, value);
      return this;
    }
  }
  /** Joins channel's socket to the given group/interface and optional source address. */
  private MembershipKey innerJoin(InetAddress group, NetworkInterface interf, InetAddress source)
      throws IOException {
    if (!group.isMulticastAddress())
      throw new IllegalArgumentException("Group not a multicast address");

    // check multicast address is compatible with this socket
    if (group instanceof Inet4Address) {
      if (family == StandardProtocolFamily.INET6 && !Net.canIPv6SocketJoinIPv4Group())
        throw new IllegalArgumentException("IPv6 socket cannot join IPv4 multicast group");
    } else if (group instanceof Inet6Address) {
      if (family != StandardProtocolFamily.INET6)
        throw new IllegalArgumentException("Only IPv6 sockets can join IPv6 multicast group");
    } else {
      throw new IllegalArgumentException("Address type not supported");
    }

    // check source address
    if (source != null) {
      if (source.isAnyLocalAddress())
        throw new IllegalArgumentException("Source address is a wildcard address");
      if (source.isMulticastAddress())
        throw new IllegalArgumentException("Source address is multicast address");
      if (source.getClass() != group.getClass())
        throw new IllegalArgumentException("Source address is different type to group");
    }

    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkMulticast(group);

    synchronized (stateLock) {
      if (!isOpen()) throw new ClosedChannelException();

      // check the registry to see if we are already a member of the group
      if (registry == null) {
        registry = new MembershipRegistry();
      } else {
        // return existing membership key
        MembershipKey key = registry.checkMembership(group, interf, source);
        if (key != null) return key;
      }

      MembershipKeyImpl key;
      if ((family == StandardProtocolFamily.INET6)
          && ((group instanceof Inet6Address) || Net.canJoin6WithIPv4Group())) {
        int index = interf.getIndex();
        if (index == -1) throw new IOException("Network interface cannot be identified");

        // need multicast and source address as byte arrays
        byte[] groupAddress = Net.inet6AsByteArray(group);
        byte[] sourceAddress = (source == null) ? null : Net.inet6AsByteArray(source);

        // join the group
        int n = Net.join6(fd, groupAddress, index, sourceAddress);
        if (n == IOStatus.UNAVAILABLE) throw new UnsupportedOperationException();

        key =
            new MembershipKeyImpl.Type6(
                this, group, interf, source, groupAddress, index, sourceAddress);

      } else {
        // need IPv4 address to identify interface
        Inet4Address target = Net.anyInet4Address(interf);
        if (target == null) throw new IOException("Network interface not configured for IPv4");

        int groupAddress = Net.inet4AsInt(group);
        int targetAddress = Net.inet4AsInt(target);
        int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source);

        // join the group
        int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress);
        if (n == IOStatus.UNAVAILABLE) throw new UnsupportedOperationException();

        key =
            new MembershipKeyImpl.Type4(
                this, group, interf, source, groupAddress, targetAddress, sourceAddress);
      }

      registry.add(key);
      return key;
    }
  }