8235193: (dc) Remove JNI overhead from DatagramChannel.send implementation
Reviewed-by: chegar
This commit is contained in:
parent
386b387ef2
commit
e7d68cd13b
@ -310,6 +310,13 @@ class Inet4Address extends InetAddress {
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 32-bit IPv4 address.
|
||||
*/
|
||||
int addressValue() {
|
||||
return holder().getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP address string in textual presentation form.
|
||||
*
|
||||
|
@ -793,6 +793,7 @@ class Inet6Address extends InetAddress {
|
||||
public boolean isMCOrgLocal() {
|
||||
return holder6.isMCOrgLocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw IP address of this {@code InetAddress} object. The result
|
||||
* is in network byte order: the highest order byte of the address is in
|
||||
@ -805,6 +806,13 @@ class Inet6Address extends InetAddress {
|
||||
return holder6.ipaddress.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the byte[] with the IPv6 address.
|
||||
*/
|
||||
byte[] addressBytes() {
|
||||
return holder6.ipaddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric scopeId, if this instance is associated with
|
||||
* an interface. If no scoped_id is set, the returned value is zero.
|
||||
@ -814,7 +822,7 @@ class Inet6Address extends InetAddress {
|
||||
* @since 1.5
|
||||
*/
|
||||
public int getScopeId() {
|
||||
return holder6.scope_id;
|
||||
return holder6.scope_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -825,7 +833,7 @@ class Inet6Address extends InetAddress {
|
||||
* @since 1.5
|
||||
*/
|
||||
public NetworkInterface getScopedInterface() {
|
||||
return holder6.scope_ifname;
|
||||
return holder6.scope_ifname;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -326,10 +326,18 @@ public class InetAddress implements java.io.Serializable {
|
||||
|
||||
public InetAddress getByName(String hostName,
|
||||
InetAddress hostAddress)
|
||||
throws UnknownHostException
|
||||
throws UnknownHostException
|
||||
{
|
||||
return InetAddress.getByName(hostName, hostAddress);
|
||||
}
|
||||
|
||||
public int addressValue(Inet4Address inet4Address) {
|
||||
return inet4Address.addressValue();
|
||||
}
|
||||
|
||||
public byte[] addressBytes(Inet6Address inet6Address) {
|
||||
return inet6Address.addressBytes();
|
||||
}
|
||||
}
|
||||
);
|
||||
init();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,8 @@
|
||||
|
||||
package jdk.internal.access;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@ -43,4 +45,14 @@ public interface JavaNetInetAddressAccess {
|
||||
*/
|
||||
InetAddress getByName(String hostName, InetAddress hostAddress)
|
||||
throws UnknownHostException;
|
||||
|
||||
/**
|
||||
* Returns the 32-bit IPv4 address.
|
||||
*/
|
||||
int addressValue(Inet4Address inet4Address);
|
||||
|
||||
/**
|
||||
* Returns a reference to the byte[] with the IPv6 address.
|
||||
*/
|
||||
byte[] addressBytes(Inet6Address inet6Address);
|
||||
}
|
||||
|
@ -91,8 +91,15 @@ class DatagramChannelImpl
|
||||
private final FileDescriptor fd;
|
||||
private final int fdVal;
|
||||
|
||||
// Native buffer for socket address used by receive0, protected by readLock
|
||||
private final NativeSocketAddress socketAddress;
|
||||
// Native sockaddrs and cached InetSocketAddress for receive, protected by readLock
|
||||
private NativeSocketAddress sourceSockAddr;
|
||||
private NativeSocketAddress cachedSockAddr;
|
||||
private InetSocketAddress cachedInetSocketAddress;
|
||||
|
||||
// Native sockaddr and cached objects for send, protected by writeLock
|
||||
private final NativeSocketAddress targetSockAddr;
|
||||
private InetSocketAddress previousTarget;
|
||||
private int previousSockAddrLength;
|
||||
|
||||
// Cleaner to close file descriptor and free native socket address
|
||||
private final Cleanable cleaner;
|
||||
@ -150,59 +157,57 @@ class DatagramChannelImpl
|
||||
|
||||
// -- End of fields protected by stateLock
|
||||
|
||||
public DatagramChannelImpl(SelectorProvider sp)
|
||||
throws IOException
|
||||
{
|
||||
super(sp);
|
||||
|
||||
this.socketAddress = new NativeSocketAddress();
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
try {
|
||||
this.family = Net.isIPv6Available()
|
||||
? StandardProtocolFamily.INET6
|
||||
: StandardProtocolFamily.INET;
|
||||
this.fd = Net.socket(family, false);
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
} catch (IOException ioe) {
|
||||
ResourceManager.afterUdpClose();
|
||||
socketAddress.free();
|
||||
throw ioe;
|
||||
}
|
||||
|
||||
Runnable releaser = releaserFor(fd, socketAddress);
|
||||
this.cleaner = CleanerFactory.cleaner().register(this, releaser);
|
||||
public DatagramChannelImpl(SelectorProvider sp) throws IOException {
|
||||
this(sp, (Net.isIPv6Available()
|
||||
? StandardProtocolFamily.INET6
|
||||
: StandardProtocolFamily.INET));
|
||||
}
|
||||
|
||||
public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family)
|
||||
throws IOException
|
||||
{
|
||||
super(sp);
|
||||
|
||||
Objects.requireNonNull(family, "'family' is null");
|
||||
if ((family != StandardProtocolFamily.INET) &&
|
||||
(family != StandardProtocolFamily.INET6)) {
|
||||
(family != StandardProtocolFamily.INET6)) {
|
||||
throw new UnsupportedOperationException("Protocol family not supported");
|
||||
}
|
||||
if (family == StandardProtocolFamily.INET6) {
|
||||
if (!Net.isIPv6Available()) {
|
||||
throw new UnsupportedOperationException("IPv6 not available");
|
||||
if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) {
|
||||
throw new UnsupportedOperationException("IPv6 not available");
|
||||
}
|
||||
|
||||
FileDescriptor fd = null;
|
||||
NativeSocketAddress[] sockAddrs = null;
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
boolean initialized = false;
|
||||
try {
|
||||
this.family = family;
|
||||
this.fd = fd = Net.socket(family, false);
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
|
||||
sockAddrs = NativeSocketAddress.allocate(3);
|
||||
readLock.lock();
|
||||
try {
|
||||
this.sourceSockAddr = sockAddrs[0];
|
||||
this.cachedSockAddr = sockAddrs[1];
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
this.targetSockAddr = sockAddrs[2];
|
||||
|
||||
initialized = true;
|
||||
} finally {
|
||||
if (!initialized) {
|
||||
if (sockAddrs != null) NativeSocketAddress.freeAll(sockAddrs);
|
||||
if (fd != null) nd.close(fd);
|
||||
ResourceManager.afterUdpClose();
|
||||
}
|
||||
}
|
||||
|
||||
this.socketAddress = new NativeSocketAddress();
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
try {
|
||||
this.family = family;
|
||||
this.fd = Net.socket(family, false);
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
} catch (IOException ioe) {
|
||||
ResourceManager.afterUdpClose();
|
||||
socketAddress.free();
|
||||
throw ioe;
|
||||
}
|
||||
|
||||
Runnable releaser = releaserFor(fd, socketAddress);
|
||||
Runnable releaser = releaserFor(fd, sockAddrs);
|
||||
this.cleaner = CleanerFactory.cleaner().register(this, releaser);
|
||||
}
|
||||
|
||||
@ -211,23 +216,37 @@ class DatagramChannelImpl
|
||||
{
|
||||
super(sp);
|
||||
|
||||
NativeSocketAddress[] sockAddrs = null;
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
boolean initialized = false;
|
||||
try {
|
||||
this.socketAddress = new NativeSocketAddress();
|
||||
} catch (OutOfMemoryError e) {
|
||||
nd.close(fd);
|
||||
throw e;
|
||||
this.family = Net.isIPv6Available()
|
||||
? StandardProtocolFamily.INET6
|
||||
: StandardProtocolFamily.INET;
|
||||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
|
||||
sockAddrs = NativeSocketAddress.allocate(3);
|
||||
readLock.lock();
|
||||
try {
|
||||
this.sourceSockAddr = sockAddrs[0];
|
||||
this.cachedSockAddr = sockAddrs[1];
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
this.targetSockAddr = sockAddrs[2];
|
||||
|
||||
initialized = true;
|
||||
} finally {
|
||||
if (!initialized) {
|
||||
if (sockAddrs != null) NativeSocketAddress.freeAll(sockAddrs);
|
||||
nd.close(fd);
|
||||
ResourceManager.afterUdpClose();
|
||||
}
|
||||
}
|
||||
|
||||
// increment UDP count to match decrement when closing
|
||||
ResourceManager.beforeUdpCreate();
|
||||
|
||||
this.family = Net.isIPv6Available()
|
||||
? StandardProtocolFamily.INET6
|
||||
: StandardProtocolFamily.INET;
|
||||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
|
||||
Runnable releaser = releaserFor(fd, socketAddress);
|
||||
Runnable releaser = releaserFor(fd, sockAddrs);
|
||||
this.cleaner = CleanerFactory.cleaner().register(this, releaser);
|
||||
|
||||
synchronized (stateLock) {
|
||||
@ -494,7 +513,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
if (n >= 0) {
|
||||
// sender address is in socket address buffer
|
||||
sender = socketAddress.toInetSocketAddress();
|
||||
sender = sourceSocketAddress();
|
||||
}
|
||||
} else {
|
||||
// security manager and unconnected
|
||||
@ -534,7 +553,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
if (n >= 0) {
|
||||
// sender address is in socket address buffer
|
||||
InetSocketAddress isa = socketAddress.toInetSocketAddress();
|
||||
InetSocketAddress isa = sourceSocketAddress();
|
||||
try {
|
||||
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
|
||||
bb.flip();
|
||||
@ -613,7 +632,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
if (n >= 0) {
|
||||
// sender address is in socket address buffer
|
||||
sender = socketAddress.toInetSocketAddress();
|
||||
sender = sourceSocketAddress();
|
||||
}
|
||||
return sender;
|
||||
} finally {
|
||||
@ -650,7 +669,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
if (n >= 0) {
|
||||
// sender address is in socket address buffer
|
||||
sender = socketAddress.toInetSocketAddress();
|
||||
sender = sourceSocketAddress();
|
||||
}
|
||||
return sender;
|
||||
} finally {
|
||||
@ -692,13 +711,32 @@ class DatagramChannelImpl
|
||||
{
|
||||
int n = receive0(fd,
|
||||
((DirectBuffer)bb).address() + pos, rem,
|
||||
socketAddress.address(),
|
||||
sourceSockAddr.address(),
|
||||
connected);
|
||||
if (n > 0)
|
||||
bb.position(pos + n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InetSocketAddress to represent the source/sender socket address
|
||||
* in sourceSockAddr. Returns the cached InetSocketAddress if the source
|
||||
* address is the same as the cached address.
|
||||
*/
|
||||
private InetSocketAddress sourceSocketAddress() throws IOException {
|
||||
assert readLock.isHeldByCurrentThread();
|
||||
if (cachedInetSocketAddress != null && sourceSockAddr.equals(cachedSockAddr)) {
|
||||
return cachedInetSocketAddress;
|
||||
}
|
||||
InetSocketAddress isa = sourceSockAddr.decode();
|
||||
// swap sourceSockAddr and cachedSockAddr
|
||||
NativeSocketAddress tmp = cachedSockAddr;
|
||||
cachedSockAddr = sourceSockAddr;
|
||||
sourceSockAddr = tmp;
|
||||
cachedInetSocketAddress = isa;
|
||||
return isa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int send(ByteBuffer src, SocketAddress target)
|
||||
throws IOException
|
||||
@ -709,7 +747,8 @@ class DatagramChannelImpl
|
||||
writeLock.lock();
|
||||
try {
|
||||
boolean blocking = isBlocking();
|
||||
int n = 0;
|
||||
int n;
|
||||
boolean completed = false;
|
||||
try {
|
||||
SocketAddress remote = beginWrite(blocking, false);
|
||||
if (remote != null) {
|
||||
@ -724,6 +763,7 @@ class DatagramChannelImpl
|
||||
n = IOUtil.write(fd, src, -1, nd);
|
||||
}
|
||||
}
|
||||
completed = (n > 0);
|
||||
} else {
|
||||
// not connected
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
@ -744,11 +784,12 @@ class DatagramChannelImpl
|
||||
n = send(fd, src, isa);
|
||||
}
|
||||
}
|
||||
completed = (n >= 0);
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
endWrite(blocking, completed);
|
||||
}
|
||||
assert n >= 0 || n == IOStatus.UNAVAILABLE;
|
||||
return IOStatus.normalize(n);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
@ -813,11 +854,11 @@ class DatagramChannelImpl
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
|
||||
boolean preferIPv6 = (family != StandardProtocolFamily.INET);
|
||||
int written;
|
||||
try {
|
||||
written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos,
|
||||
rem, target.getAddress(), target.getPort());
|
||||
int addressLen = targetSocketAddress(target);
|
||||
written = send0(fd, ((DirectBuffer)bb).address() + pos, rem,
|
||||
targetSockAddr.address(), addressLen);
|
||||
} catch (PortUnreachableException pue) {
|
||||
if (isConnected())
|
||||
throw pue;
|
||||
@ -828,6 +869,23 @@ class DatagramChannelImpl
|
||||
return written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given InetSocketAddress into targetSockAddr, returning the
|
||||
* length of the sockaddr structure (sizeof struct sockaddr or sockaddr6).
|
||||
*/
|
||||
private int targetSocketAddress(InetSocketAddress isa) {
|
||||
assert writeLock.isHeldByCurrentThread();
|
||||
// Nothing to do if target address is already in the buffer. Use
|
||||
// identity rather than equals as Inet6Address.equals ignores scope_id.
|
||||
if (isa == previousTarget)
|
||||
return previousSockAddrLength;
|
||||
previousTarget = null;
|
||||
int len = targetSockAddr.encode(family, isa);
|
||||
previousTarget = isa;
|
||||
previousSockAddrLength = len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer buf) throws IOException {
|
||||
Objects.requireNonNull(buf);
|
||||
@ -1488,8 +1546,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
|
||||
/**
|
||||
* Block datagrams from given source if a memory to receive all
|
||||
* datagrams.
|
||||
* Block datagrams from the given source.
|
||||
*/
|
||||
void block(MembershipKeyImpl key, InetAddress source)
|
||||
throws IOException
|
||||
@ -1527,7 +1584,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
|
||||
/**
|
||||
* Unblock given source.
|
||||
* Unblock the given source.
|
||||
*/
|
||||
void unblock(MembershipKeyImpl key, InetAddress source) {
|
||||
assert key.channel() == this;
|
||||
@ -1731,10 +1788,9 @@ class DatagramChannelImpl
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an action to release a the given file descriptor and free the
|
||||
* given native socket address.
|
||||
* Returns an action to release the given file descriptor and socket addresses.
|
||||
*/
|
||||
private static Runnable releaserFor(FileDescriptor fd, NativeSocketAddress sa) {
|
||||
private static Runnable releaserFor(FileDescriptor fd, NativeSocketAddress... sockAddrs) {
|
||||
return () -> {
|
||||
try {
|
||||
nd.close(fd);
|
||||
@ -1743,7 +1799,7 @@ class DatagramChannelImpl
|
||||
} finally {
|
||||
// decrement socket count and release memory
|
||||
ResourceManager.afterUdpClose();
|
||||
sa.free();
|
||||
NativeSocketAddress.freeAll(sockAddrs);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1757,8 +1813,8 @@ class DatagramChannelImpl
|
||||
long senderAddress, boolean connected)
|
||||
throws IOException;
|
||||
|
||||
private static native int send0(boolean preferIPv6, FileDescriptor fd,
|
||||
long address, int len, InetAddress addr, int port)
|
||||
private static native int send0(FileDescriptor fd, long address, int len,
|
||||
long targetAddress, int targetAddressLen)
|
||||
throws IOException;
|
||||
|
||||
static {
|
||||
|
@ -25,12 +25,18 @@
|
||||
|
||||
package sun.nio.ch;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProtocolFamily;
|
||||
import java.net.SocketException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.channels.UnsupportedAddressTypeException;
|
||||
|
||||
import jdk.internal.access.JavaNetInetAddressAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
|
||||
@ -41,13 +47,16 @@ import jdk.internal.util.ArraysSupport;
|
||||
* This class is not thread safe.
|
||||
*/
|
||||
class NativeSocketAddress {
|
||||
private static final JavaNetInetAddressAccess JNINA = SharedSecrets.getJavaNetInetAddressAccess();
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
private static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
|
||||
|
||||
private static final int AF_INET = AFINET();
|
||||
private static final int AF_INET6 = AFINET6();
|
||||
|
||||
private static final int SIZEOF_SOCKETADDRESS = sizeofSOCKETADDRESS();
|
||||
private static final int SIZEOF_SOCKADDR4 = sizeofSockAddr4();
|
||||
private static final int SIZEOF_SOCKADDR6 = sizeofSockAddr6();
|
||||
private static final int SIZEOF_SOCKETADDRESS = Math.max(SIZEOF_SOCKADDR4, SIZEOF_SOCKADDR6);
|
||||
private static final int SIZEOF_FAMILY = sizeofFamily();
|
||||
private static final int OFFSET_FAMILY = offsetFamily();
|
||||
private static final int OFFSET_SIN4_PORT = offsetSin4Port();
|
||||
@ -55,77 +64,127 @@ class NativeSocketAddress {
|
||||
private static final int OFFSET_SIN6_PORT = offsetSin6Port();
|
||||
private static final int OFFSET_SIN6_ADDR = offsetSin6Addr();
|
||||
private static final int OFFSET_SIN6_SCOPE_ID = offsetSin6ScopeId();
|
||||
private static final int OFFSET_SIN6_FLOWINFO = offsetSin6FlowInfo();
|
||||
|
||||
// SOCKETADDRESS
|
||||
private final long address;
|
||||
|
||||
// cached copy of SOCKETADDRESS and the corresponding InetSocketAddress
|
||||
private final long cachedSocketAddress;
|
||||
private InetSocketAddress cachedInetSocketAddress;
|
||||
|
||||
NativeSocketAddress() {
|
||||
// allocate 2 * SOCKETADDRESS
|
||||
int size = SIZEOF_SOCKETADDRESS << 1;
|
||||
long base = UNSAFE.allocateMemory(size);
|
||||
UNSAFE.setMemory(base, size, (byte) 0);
|
||||
|
||||
this.address = base;
|
||||
this.cachedSocketAddress = base + SIZEOF_SOCKETADDRESS;
|
||||
}
|
||||
|
||||
long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
void free() {
|
||||
UNSAFE.freeMemory(address);
|
||||
NativeSocketAddress() {
|
||||
long base = UNSAFE.allocateMemory(SIZEOF_SOCKETADDRESS);
|
||||
UNSAFE.setMemory(base, SIZEOF_SOCKETADDRESS, (byte) 0);
|
||||
this.address = base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an array of native socket addresses.
|
||||
*/
|
||||
static NativeSocketAddress[] allocate(int count) {
|
||||
NativeSocketAddress[] array = new NativeSocketAddress[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
array[i] = new NativeSocketAddress();
|
||||
} catch (OutOfMemoryError e) {
|
||||
freeAll(array);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free all non-null native socket addresses in the given array.
|
||||
*/
|
||||
static void freeAll(NativeSocketAddress[] array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
NativeSocketAddress sa = array[i];
|
||||
if (sa != null) {
|
||||
UNSAFE.freeMemory(sa.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given InetSocketAddress into this socket address.
|
||||
* @param protocolFamily protocol family
|
||||
* @param isa the InetSocketAddress to encode
|
||||
* @return the size of the socket address (sizeof sockaddr or sockaddr6)
|
||||
* @throws UnsupportedAddressTypeException if the address type is not supported
|
||||
*/
|
||||
int encode(ProtocolFamily protocolFamily, InetSocketAddress isa) {
|
||||
if (protocolFamily == StandardProtocolFamily.INET) {
|
||||
// struct sockaddr
|
||||
InetAddress ia = isa.getAddress();
|
||||
if (!(ia instanceof Inet4Address))
|
||||
throw new UnsupportedAddressTypeException();
|
||||
putFamily(AF_INET);
|
||||
putAddress(AF_INET, ia);
|
||||
putPort(AF_INET, isa.getPort());
|
||||
return SIZEOF_SOCKADDR4;
|
||||
} else {
|
||||
// struct sockaddr6
|
||||
putFamily(AF_INET6);
|
||||
putAddress(AF_INET6, isa.getAddress());
|
||||
putPort(AF_INET6, isa.getPort());
|
||||
UNSAFE.putInt(address + OFFSET_SIN6_FLOWINFO, 0);
|
||||
return SIZEOF_SOCKADDR6;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InetSocketAddress to represent the socket address in this buffer.
|
||||
* @throws SocketException if the socket address is not AF_INET or AF_INET6
|
||||
*/
|
||||
InetSocketAddress toInetSocketAddress() throws SocketException {
|
||||
// return cached InetSocketAddress if the SOCKETADDRESS bytes match
|
||||
if (cachedInetSocketAddress != null && mismatch() < 0) {
|
||||
return cachedInetSocketAddress;
|
||||
}
|
||||
|
||||
// decode SOCKETADDRESS to InetSocketAddress
|
||||
InetSocketAddress decode() throws SocketException {
|
||||
int family = family();
|
||||
if (family != AF_INET && family != AF_INET6)
|
||||
throw new SocketException("Socket family not recognized");
|
||||
var isa = new InetSocketAddress(address(family), port(family));
|
||||
|
||||
// copy SOCKETADDRESS and InetSocketAddress
|
||||
UNSAFE.copyMemory(null, address, null, cachedSocketAddress, SIZEOF_SOCKETADDRESS);
|
||||
this.cachedInetSocketAddress = isa;
|
||||
return isa;
|
||||
return new InetSocketAddress(address(family), port(family));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a mismatch between the SOCKETADDRESS structures stored at address
|
||||
* and cachedSocketAddress.
|
||||
* Find a mismatch between this and another socket address
|
||||
* @return the byte offset of the first mismatch or -1 if no mismatch
|
||||
*/
|
||||
private int mismatch() {
|
||||
private int mismatch(NativeSocketAddress other) {
|
||||
int i = ArraysSupport.vectorizedMismatch(null,
|
||||
address,
|
||||
this.address,
|
||||
null,
|
||||
cachedSocketAddress,
|
||||
other.address,
|
||||
SIZEOF_SOCKETADDRESS,
|
||||
ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
i = SIZEOF_SOCKETADDRESS - ~i;
|
||||
for (; i < SIZEOF_SOCKETADDRESS; i++) {
|
||||
if (UNSAFE.getByte(address + i) != UNSAFE.getByte(cachedSocketAddress + i)) {
|
||||
if (UNSAFE.getByte(this.address + i) != UNSAFE.getByte(other.address + i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof NativeSocketAddress) {
|
||||
return mismatch((NativeSocketAddress) other) < 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 0;
|
||||
for (int offset = 0; offset < SIZEOF_SOCKETADDRESS; offset++) {
|
||||
h = 31 * h + UNSAFE.getByte(address + offset);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int family = family();
|
||||
@ -151,7 +210,20 @@ class NativeSocketAddress {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the sin4_port or sin6_port field. These fields are
|
||||
* Stores the given family in the sa_family field.
|
||||
*/
|
||||
private void putFamily(int family) {
|
||||
if (SIZEOF_FAMILY == 1) {
|
||||
UNSAFE.putByte(address + OFFSET_FAMILY, (byte) family);
|
||||
} else if (SIZEOF_FAMILY == 2) {
|
||||
UNSAFE.putShort(address + OFFSET_FAMILY, (short) family);
|
||||
} else {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the sin_port or sin6_port field. These fields are
|
||||
* stored in network order.
|
||||
*/
|
||||
private int port(int family) {
|
||||
@ -166,9 +238,26 @@ class NativeSocketAddress {
|
||||
return (Byte.toUnsignedInt(b1) << 8) + Byte.toUnsignedInt(b2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given port number in the sin_port or sin6_port field. The
|
||||
* port is stored in network order.
|
||||
*/
|
||||
private void putPort(int family, int port) {
|
||||
byte b1 = (byte) ((port >> 8) & 0xff);
|
||||
byte b2 = (byte) ((port >> 0) & 0xff);
|
||||
if (family == AF_INET) {
|
||||
UNSAFE.putByte(address + OFFSET_SIN4_PORT, b1);
|
||||
UNSAFE.putByte(address + OFFSET_SIN4_PORT + 1, b2);
|
||||
} else {
|
||||
UNSAFE.putByte(address + OFFSET_SIN6_PORT, b1);
|
||||
UNSAFE.putByte(address + OFFSET_SIN6_PORT + 1, b2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InetAddress to represent the value of the address in the
|
||||
* sin4_addr or sin6_addr fields.
|
||||
* sin4_addr or sin6_addr fields. For IPv6 addresses, the Inet6Address is
|
||||
* created with the sin6_scope_id in the sockaddr_in6 structure.
|
||||
*/
|
||||
private InetAddress address(int family) {
|
||||
int len;
|
||||
@ -196,9 +285,52 @@ class NativeSocketAddress {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given InetAddress in the sin_addr or sin6_addr/sin6_scope_id
|
||||
* fields. For IPv6 addresses, the sin6_addr will be popluated with an
|
||||
* IPv4-mapped IPv6 address when the given InetAddress is an IPv4 address.
|
||||
*/
|
||||
private void putAddress(int family, InetAddress ia) {
|
||||
if (family == AF_INET) {
|
||||
// IPv4 address
|
||||
putAddress(address + OFFSET_SIN4_ADDR, (Inet4Address) ia);
|
||||
} else {
|
||||
int scope_id;
|
||||
if (ia instanceof Inet4Address) {
|
||||
// IPv4-mapped IPv6 address
|
||||
UNSAFE.setMemory(address + OFFSET_SIN6_ADDR, 10, (byte) 0);
|
||||
UNSAFE.putByte(address + OFFSET_SIN6_ADDR + 10, (byte) 0xff);
|
||||
UNSAFE.putByte(address + OFFSET_SIN6_ADDR + 11, (byte) 0xff);
|
||||
putAddress(address + OFFSET_SIN6_ADDR + 12, (Inet4Address) ia);
|
||||
scope_id = 0;
|
||||
} else {
|
||||
// IPv6 address
|
||||
var inet6Address = (Inet6Address) ia;
|
||||
putAddress(address + OFFSET_SIN6_ADDR, inet6Address);
|
||||
scope_id = inet6Address.getScopeId();
|
||||
}
|
||||
UNSAFE.putInt(address + OFFSET_SIN6_SCOPE_ID, scope_id);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putAddress(long address, Inet4Address ia) {
|
||||
int ipAddress = JNINA.addressValue(ia);
|
||||
// network order
|
||||
UNSAFE.putByte(address + 0, (byte) ((ipAddress >>> 24) & 0xFF));
|
||||
UNSAFE.putByte(address + 1, (byte) ((ipAddress >>> 16) & 0xFF));
|
||||
UNSAFE.putByte(address + 2, (byte) ((ipAddress >>> 8) & 0xFF));
|
||||
UNSAFE.putByte(address + 3, (byte) (ipAddress & 0xFF));
|
||||
}
|
||||
|
||||
private static void putAddress(long address, Inet6Address ia) {
|
||||
byte[] bytes = JNINA.addressBytes(ia);
|
||||
UNSAFE.copyMemory(bytes, ARRAY_BASE_OFFSET, null, address, 16);
|
||||
}
|
||||
|
||||
private static native int AFINET();
|
||||
private static native int AFINET6();
|
||||
private static native int sizeofSOCKETADDRESS();
|
||||
private static native int sizeofSockAddr4();
|
||||
private static native int sizeofSockAddr6();
|
||||
private static native int sizeofFamily();
|
||||
private static native int offsetFamily();
|
||||
private static native int offsetSin4Port();
|
||||
@ -206,6 +338,7 @@ class NativeSocketAddress {
|
||||
private static native int offsetSin6Port();
|
||||
private static native int offsetSin6Addr();
|
||||
private static native int offsetSin6ScopeId();
|
||||
private static native int offsetSin6FlowInfo();
|
||||
|
||||
static {
|
||||
IOUtil.load();
|
||||
|
@ -40,11 +40,17 @@
|
||||
return AF_INET6;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_NativeSocketAddress_sizeofSOCKETADDRESS(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
return sizeof(SOCKETADDRESS);
|
||||
}
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_NativeSocketAddress_sizeofSockAddr4(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
return sizeof(struct sockaddr_in);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_NativeSocketAddress_sizeofSockAddr6(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
return sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_NativeSocketAddress_sizeofFamily(JNIEnv* env, jclass clazz)
|
||||
@ -88,3 +94,9 @@ Java_sun_nio_ch_NativeSocketAddress_sizeofFamily(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
return offsetof(struct sockaddr_in6, sin6_scope_id);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_NativeSocketAddress_offsetSin6FlowInfo(JNIEnv* env, jclass clazz)
|
||||
{
|
||||
return offsetof(struct sockaddr_in6, sin6_flowinfo);
|
||||
}
|
||||
|
@ -129,25 +129,20 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jclass clazz,
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jclass clazz,
|
||||
jboolean preferIPv6, jobject fdo, jlong address,
|
||||
jint len, jobject destAddress, jint destPort)
|
||||
jobject fdo, jlong bufAddress, jint len,
|
||||
jlong targetAddress, jint targetAddressLen)
|
||||
{
|
||||
jint fd = fdval(env, fdo);
|
||||
void *buf = (void *)jlong_to_ptr(address);
|
||||
SOCKETADDRESS sa;
|
||||
int sa_len = 0;
|
||||
jint n = 0;
|
||||
void *buf = (void *)jlong_to_ptr(bufAddress);
|
||||
SOCKETADDRESS *sa = (SOCKETADDRESS *)jlong_to_ptr(targetAddress);
|
||||
socklen_t sa_len = (socklen_t) targetAddressLen;
|
||||
jint n;
|
||||
|
||||
if (len > MAX_PACKET_LEN) {
|
||||
len = MAX_PACKET_LEN;
|
||||
}
|
||||
|
||||
if (NET_InetAddressToSockaddr(env, destAddress, destPort, &sa,
|
||||
&sa_len, preferIPv6) != 0) {
|
||||
return IOS_THROWN;
|
||||
}
|
||||
|
||||
n = sendto(fd, buf, len, 0, &sa.sa, sa_len);
|
||||
n = sendto(fd, buf, len, 0, (struct sockaddr *)sa, sa_len);
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return IOS_UNAVAILABLE;
|
||||
|
@ -145,22 +145,16 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jclass clazz,
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jclass clazz,
|
||||
jboolean preferIPv6, jobject fdo,
|
||||
jlong address, jint len,
|
||||
jobject destAddress, jint destPort)
|
||||
jobject fdo, jlong bufAddress, jint len,
|
||||
jlong targetAddress, jint targetAddressLen)
|
||||
{
|
||||
jint fd = fdval(env, fdo);
|
||||
void *buf = (void *)jlong_to_ptr(address);
|
||||
SOCKETADDRESS sa;
|
||||
int sa_len = 0;
|
||||
jint rv = 0;
|
||||
void *buf = (void *)jlong_to_ptr(bufAddress);
|
||||
SOCKETADDRESS *sa = (SOCKETADDRESS *)jlong_to_ptr(targetAddress);
|
||||
int sa_len = targetAddressLen;
|
||||
jint rv;
|
||||
|
||||
if (NET_InetAddressToSockaddr(env, destAddress, destPort, &sa,
|
||||
&sa_len, preferIPv6) != 0) {
|
||||
return IOS_THROWN;
|
||||
}
|
||||
|
||||
rv = sendto((SOCKET)fd, buf, len, 0, &sa.sa, sa_len);
|
||||
rv = sendto((SOCKET)fd, buf, len, 0,(struct sockaddr *)sa, sa_len);
|
||||
if (rv == SOCKET_ERROR) {
|
||||
int theErr = (jint)WSAGetLastError();
|
||||
if (theErr == WSAEWOULDBLOCK) {
|
||||
|
@ -22,10 +22,11 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8234805
|
||||
* @summary Test that DatagramChannel.receive returns the expected sender address
|
||||
* @run main ManySenders
|
||||
* @run main/othervm -Djava.net.preferIPv4Stack=true ManySenders
|
||||
* @bug 8234805 8235193
|
||||
* @summary Test DatagramChannel send/receive and that receive returns the expected
|
||||
* sender address
|
||||
* @run main ManySourcesAndTargets
|
||||
* @run main/othervm -Djava.net.preferIPv4Stack=true ManySourcesAndTargets
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -36,38 +37,59 @@ import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ManySenders {
|
||||
public class ManySourcesAndTargets {
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// use addresses on interfaces that have the loopback and local host
|
||||
InetAddress lh = InetAddress.getLocalHost();
|
||||
InetAddress lb = InetAddress.getLoopbackAddress();
|
||||
List<InetAddress> addresses = Stream.concat(
|
||||
NetworkInterface.getByInetAddress(lh).inetAddresses(),
|
||||
NetworkInterface.getByInetAddress(lb).inetAddresses())
|
||||
List<InetAddress> addresses = Stream.of(lh, lb)
|
||||
.map(ManySourcesAndTargets::networkInterface)
|
||||
.flatMap(Optional::stream)
|
||||
.flatMap(NetworkInterface::inetAddresses)
|
||||
.filter(ia -> !ia.isAnyLocalAddress())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// bind DatagramChannel to wildcard address so it can receive from any address
|
||||
// Test DatagramChannel.send
|
||||
try (DatagramChannel reader = DatagramChannel.open()) {
|
||||
// bind reader to wildcard address so it can receive from any address
|
||||
reader.bind(new InetSocketAddress(0));
|
||||
for (InetAddress address : addresses) {
|
||||
System.out.format("%n-- %s --%n", address.getHostAddress());
|
||||
|
||||
// send 3 datagrams from the given address to the reader
|
||||
test(3, address, reader);
|
||||
testSend(3, address, reader);
|
||||
}
|
||||
}
|
||||
|
||||
// Test DatagramChannel.receive
|
||||
try (DatagramChannel sender = DatagramChannel.open()) {
|
||||
// bind sender to wildcard address so it can send to any address
|
||||
sender.bind(new InetSocketAddress(0));
|
||||
for (InetAddress address : addresses) {
|
||||
System.out.format("%n-- %s --%n", address.getHostAddress());
|
||||
|
||||
// send 3 datagrams to a datagram bound to the given address
|
||||
testReceive(3, sender, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test(int count, InetAddress address, DatagramChannel reader) throws Exception {
|
||||
/**
|
||||
* Creates a sender DatagramChannel bound to the given address and uses it to
|
||||
* sends datagrams to the given reader. The reader receives the datagrams and
|
||||
* checks the source/sender address.
|
||||
*/
|
||||
static void testSend(int count, InetAddress address, DatagramChannel reader) throws Exception {
|
||||
int remotePort = reader.socket().getLocalPort();
|
||||
InetSocketAddress remote = new InetSocketAddress(address, remotePort);
|
||||
|
||||
@ -103,6 +125,30 @@ public class ManySenders {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reader DatagramChannel bound to the given address uses the given
|
||||
* sender to send datagrams to that reader. The reader receives the datagrams.
|
||||
*/
|
||||
static void testReceive(int count, DatagramChannel sender, InetAddress address) throws Exception {
|
||||
SocketAddress local = sender.getLocalAddress();
|
||||
|
||||
try (DatagramChannel reader = DatagramChannel.open()) {
|
||||
// bind to the given address
|
||||
reader.bind(new InetSocketAddress(address, 0));
|
||||
|
||||
SocketAddress remote = reader.getLocalAddress();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
System.out.format("send %s -> %s%n", local, remote);
|
||||
reader.send(ByteBuffer.allocate(32), remote);
|
||||
|
||||
ByteBuffer bb = ByteBuffer.allocate(1000);
|
||||
SocketAddress source = reader.receive(bb);
|
||||
System.out.format("received datagram from %s%n", source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] serialize(SocketAddress address) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
@ -116,4 +162,12 @@ public class ManySenders {
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
return (SocketAddress) ois.readObject();
|
||||
}
|
||||
|
||||
private static Optional<NetworkInterface> networkInterface(InetAddress ia) {
|
||||
try {
|
||||
return Optional.ofNullable(NetworkInterface.getByInetAddress(ia));
|
||||
} catch (SocketException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user