8235193: (dc) Remove JNI overhead from DatagramChannel.send implementation

Reviewed-by: chegar
This commit is contained in:
Alan Bateman 2019-12-04 16:37:00 +00:00
parent 386b387ef2
commit e7d68cd13b
10 changed files with 438 additions and 159 deletions

View File

@ -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.
*

View File

@ -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;
}
/**

View File

@ -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();

View File

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

View File

@ -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 {

View File

@ -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();

View File

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

View File

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

View File

@ -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) {

View File

@ -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();
}
}
}