Use byte[] to create DatagramSocketAddress and so reduce overhead (#9516)

Motivation:

At the moment we use the String representation of the IP to create the DatagramSocketAddress. This is not for free and we should better use the byte[] directly to reduce the overhead of parsing the String (and creating it in the first place)

Modifications:

Directly use byte[] as input for the DatagramSocketAddress

Result:

Less overhead when using Datagrams with native transports
This commit is contained in:
Norman Maurer 2019-08-30 09:22:44 +02:00 committed by GitHub
parent b7e9829a49
commit 291f80733a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 41 deletions

View File

@ -41,7 +41,6 @@ static jmethodID datagramSocketAddrMethodId = NULL;
static jmethodID inetSocketAddrMethodId = NULL;
static jclass inetSocketAddressClass = NULL;
static int socketType = AF_INET;
static const char* ip4prefix = "::ffff:";
static const unsigned char wildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const unsigned char ipv4MappedWildcardAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
@ -68,47 +67,60 @@ static int nettyNonBlockingSocket(int domain, int type, int protocol) {
#endif
}
static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr, int len, jobject local) {
char ipstr[INET6_ADDRSTRLEN];
int port;
jstring ipString;
static int ipAddressLength(const struct sockaddr_storage* addr) {
if (addr->ss_family == AF_INET) {
struct sockaddr_in* s = (struct sockaddr_in*) addr;
port = ntohs(s->sin_port);
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
ipString = (*env)->NewStringUTF(env, ipstr);
} else {
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
if (strncasecmp(ipstr, ip4prefix, 7) == 0) {
// IPv4-mapped-on-IPv6.
// Cut of ::ffff: prefix to workaround performance issues when parsing these
// addresses in InetAddress.getByName(...).
//
// See https://github.com/netty/netty/issues/2867
ipString = (*env)->NewStringUTF(env, &ipstr[7]);
} else {
ipString = (*env)->NewStringUTF(env, ipstr);
}
}
jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len, local);
return socketAddr;
}
static jsize addressLength(const struct sockaddr_storage* addr) {
if (addr->ss_family == AF_INET) {
return 8;
return 4;
}
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
if (s->sin6_addr.s6_addr[11] == 0xff && s->sin6_addr.s6_addr[10] == 0xff &&
s->sin6_addr.s6_addr[9] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[5] == 0x00 &&
s->sin6_addr.s6_addr[4] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[0] == 0x00) {
// IPv4-mapped-on-IPv6
return 8;
s->sin6_addr.s6_addr[9] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[5] == 0x00 &&
s->sin6_addr.s6_addr[4] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[0] == 0x00) {
// IPv4-mapped-on-IPv6
return 4;
}
return 24;
return 16;
}
static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr, int len, jobject local) {
int port;
int scopeId;
int ipLength = ipAddressLength(addr);
jbyteArray addressBytes = (*env)->NewByteArray(env, ipLength);
if (addr->ss_family == AF_INET) {
struct sockaddr_in* s = (struct sockaddr_in*) addr;
port = ntohs(s->sin_port);
scopeId = 0;
(*env)->SetByteArrayRegion(env, addressBytes, 0, ipLength, (jbyte*) &s->sin_addr.s_addr);
} else {
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
port = ntohs(s->sin6_port);
scopeId = s->sin6_scope_id;
int offset;
if (ipLength == 4) {
// IPv4-mapped-on-IPv6.
offset = 12;
} else {
offset = 0;
}
(*env)->SetByteArrayRegion(env, addressBytes, offset, ipLength, (jbyte*) &s->sin6_addr.s6_addr);
}
jobject obj = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, addressBytes, scopeId, port, len, local);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return NULL;
}
return obj;
}
static jsize addressLength(const struct sockaddr_storage* addr) {
int len = ipAddressLength(addr);
if (len == 4) {
// Only encode port into it
return len + 4;
}
// we encode port + scope into it
return len + 8;
}
static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr, jbyteArray bArray, int offset, jsize len) {
@ -378,6 +390,9 @@ static jobject _recvFrom(JNIEnv* env, jint fd, void* buffer, jint pos, jint limi
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) {
memcpy (&daddr, CMSG_DATA(cmsg), sizeof (struct sockaddr_storage));
local = createDatagramSocketAddress(env, &daddr, res, NULL);
if (local == NULL) {
return NULL;
}
break;
}
}
@ -1054,12 +1069,12 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
// Respect shading...
char parameters[1024] = {0};
snprintf(parameters, sizeof(parameters), "(Ljava/lang/String;IIL%s;)V", nettyClassName);
snprintf(parameters, sizeof(parameters), "([BIIIL%s;)V", nettyClassName);
datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "<init>", parameters);
if (datagramSocketAddrMethodId == NULL) {
char msg[1024] = {0};
snprintf(msg, sizeof(msg), "failed to get method ID: %s.<init>(String, int, int, %s)", nettyClassName, nettyClassName);
snprintf(msg, sizeof(msg), "failed to get method ID: %s.<init>(byte[], int, int, int, %s)", nettyClassName, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
netty_unix_errors_throwRuntimeException(env, msg);

View File

@ -15,7 +15,10 @@
*/
package io.netty.channel.unix;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
/**
* Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need
@ -30,8 +33,9 @@ public final class DatagramSocketAddress extends InetSocketAddress {
private final int receivedAmount;
private final DatagramSocketAddress localAddress;
DatagramSocketAddress(String addr, int port, int receivedAmount, DatagramSocketAddress local) {
super(addr, port);
DatagramSocketAddress(byte[] addr, int scopeId, int port, int receivedAmount, DatagramSocketAddress local)
throws UnknownHostException {
super(newAddress(addr, scopeId), port);
this.receivedAmount = receivedAmount;
localAddress = local;
}
@ -43,4 +47,11 @@ public final class DatagramSocketAddress extends InetSocketAddress {
public int receivedAmount() {
return receivedAmount;
}
private static InetAddress newAddress(byte[] bytes, int scopeId) throws UnknownHostException {
if (bytes.length == 4) {
return InetAddress.getByAddress(bytes);
}
return Inet6Address.getByAddress(null, bytes, scopeId);
}
}