From 291f80733ac84a2a45301bffc946e4c5522cd558 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 30 Aug 2019 09:22:44 +0200 Subject: [PATCH] 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 --- .../src/main/c/netty_unix_socket.c | 93 +++++++++++-------- .../channel/unix/DatagramSocketAddress.java | 15 ++- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index d1d850a86e..67f22058fa 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -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, "", parameters); if (datagramSocketAddrMethodId == NULL) { char msg[1024] = {0}; - snprintf(msg, sizeof(msg), "failed to get method ID: %s.(String, int, int, %s)", nettyClassName, nettyClassName); + snprintf(msg, sizeof(msg), "failed to get method ID: %s.(byte[], int, int, int, %s)", nettyClassName, nettyClassName); free(nettyClassName); nettyClassName = NULL; netty_unix_errors_throwRuntimeException(env, msg); diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java index 874d7054f5..131557c2a7 100644 --- a/transport-native-unix-common/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java @@ -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); + } }