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; } }