Fixed Native library issues where it couldn't be loaded and would crash

This commit is contained in:
nathan 2018-02-09 22:36:36 +01:00
parent 3d6f4d81f8
commit bf681688d0
6 changed files with 208 additions and 60 deletions

View File

@ -15,12 +15,7 @@
*/
package dorkbox.network;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@ -31,15 +26,19 @@ import org.slf4j.Logger;
import dorkbox.network.pipeline.discovery.ClientDiscoverHostHandler;
import dorkbox.network.pipeline.discovery.ClientDiscoverHostInitializer;
import dorkbox.util.OS;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
@SuppressWarnings({"unused", "AutoBoxing"})
public final
@ -154,9 +153,21 @@ class Broadcast {
logger2.info("Searching for host on {} : {}", address, udpPort);
}
NioEventLoopGroup group = new NioEventLoopGroup();
EventLoopGroup group;
Class<? extends Channel> channelClass;
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
group = new OioEventLoopGroup(1);
channelClass = OioDatagramChannel.class;
} else {
group = new NioEventLoopGroup(1);
channelClass = NioDatagramChannel.class;
}
Bootstrap udpBootstrap = new Bootstrap().group(group)
.channel(NioDatagramChannel.class)
.channel(channelClass)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ClientDiscoverHostInitializer())
.localAddress(new InetSocketAddress(address,

View File

@ -53,7 +53,6 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
import io.netty.util.internal.PlatformDependent;
/**
* The client is both SYNC and ASYNC. It starts off SYNC (blocks thread until it's done), then once it's connected to the server, it's
@ -113,22 +112,19 @@ class Client<C extends Connection> extends EndPointClient implements Connection
localChannelName = config.localChannelName;
hostName = config.host;
boolean isAndroid = PlatformDependent.isAndroid();
final EventLoopGroup boss;
if (isAndroid) {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName, threadGroup));
boss = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isMacOsX()) {
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
boss = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
boss = new KQueueEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else {
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
@ -164,16 +160,16 @@ class Client<C extends Connection> extends EndPointClient implements Connection
Bootstrap tcpBootstrap = new Bootstrap();
bootstraps.add(new BootstrapWrapper("TCP", config.host, config.tcpPort, tcpBootstrap));
if (isAndroid) {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
tcpBootstrap.channel(OioSocketChannel.class);
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
tcpBootstrap.channel(EpollSocketChannel.class);
}
else if (OS.isMacOsX()) {
// JNI network stack is MUCH faster (but only on macosx)
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
tcpBootstrap.channel(KQueueSocketChannel.class);
}
else {
@ -189,7 +185,7 @@ class Client<C extends Connection> extends EndPointClient implements Connection
serializationManager));
// android screws up on this!!
tcpBootstrap.option(ChannelOption.TCP_NODELAY, !isAndroid)
tcpBootstrap.option(ChannelOption.TCP_NODELAY, !OS.isAndroid())
.option(ChannelOption.SO_KEEPALIVE, true);
}
@ -198,20 +194,19 @@ class Client<C extends Connection> extends EndPointClient implements Connection
Bootstrap udpBootstrap = new Bootstrap();
bootstraps.add(new BootstrapWrapper("UDP", config.host, config.udpPort, udpBootstrap));
if (isAndroid) {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
udpBootstrap.channel(OioDatagramChannel.class);
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
udpBootstrap.channel(EpollDatagramChannel.class);
}
else if (OS.isMacOsX()) {
// JNI network stack is MUCH faster (but only on macosx)
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
udpBootstrap.channel(KQueueDatagramChannel.class);
}
else {
// windows
udpBootstrap.channel(NioDatagramChannel.class);
}

View File

@ -50,6 +50,8 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.ReflectiveChannelFactory;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.kqueue.KQueueDatagramChannel;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
@ -59,7 +61,6 @@ import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.resolver.HostsFileEntriesResolver;
import io.netty.resolver.ResolvedAddressTypes;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.PlatformDependent;
/**
* A DnsClient for resolving DNS name, with reasonably good defaults.
@ -232,16 +233,21 @@ class DnsClient extends Shutdownable {
DnsClient(Collection<InetSocketAddress> nameServerAddresses) {
super(DnsClient.class);
if (PlatformDependent.isAndroid()) {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
eventLoopGroup = new OioEventLoopGroup(1, new NamedThreadFactory(THREAD_NAME + "-DNS", threadGroup));
channelType = OioDatagramChannel.class;
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
eventLoopGroup = new EpollEventLoopGroup(1, new NamedThreadFactory(THREAD_NAME + "-DNS", threadGroup));
channelType = EpollDatagramChannel.class;
}
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
eventLoopGroup = new KQueueEventLoopGroup(1, new NamedThreadFactory(THREAD_NAME + "-DNS", threadGroup));
channelType = KQueueDatagramChannel.class;
}
else {
eventLoopGroup = new NioEventLoopGroup(1, new NamedThreadFactory(THREAD_NAME + "-DNS", threadGroup));
channelType = NioDatagramChannel.class;

View File

@ -30,7 +30,6 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
@ -43,7 +42,6 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import io.netty.channel.unix.UnixChannelOption;
/**
* from: https://blog.cloudflare.com/how-the-consumer-product-safety-commission-is-inadvertently-behind-the-internets-largest-ddos-attacks/
@ -117,20 +115,21 @@ class DnsServer extends Shutdownable {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new OioEventLoopGroup(0, new NamedThreadFactory(threadName, threadGroup));
boss = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
boss = new EpollEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new EpollEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isMacOsX()) {
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
boss = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else {
// sometimes the native libraries cannot be loaded, so fall back to NIO
boss = new NioEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new NioEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
@ -148,11 +147,11 @@ class DnsServer extends Shutdownable {
// android ONLY supports OIO (not NIO)
tcpBootstrap.channel(OioServerSocketChannel.class);
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
tcpBootstrap.channel(EpollServerSocketChannel.class);
}
else if (OS.isMacOsX()) {
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
tcpBootstrap.channel(KQueueServerSocketChannel.class);
}
@ -165,7 +164,6 @@ class DnsServer extends Shutdownable {
tcpBootstrap.group(boss, worker)
.option(ChannelOption.SO_BACKLOG, backlogConnectionCount)
.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(EndPoint.WRITE_BUFF_LOW, EndPoint.WRITE_BUFF_HIGH))
@ -191,13 +189,11 @@ class DnsServer extends Shutdownable {
}
else if (OS.isLinux()) {
// JNI network stack is MUCH faster (but only on linux)
udpBootstrap.channel(EpollDatagramChannel.class)
.option(EpollChannelOption.SO_REUSEPORT, true);
udpBootstrap.channel(EpollDatagramChannel.class);
}
else if (OS.isMacOsX()) {
// JNI network stack is MUCH faster (but only on macosx)
udpBootstrap.channel(KQueueDatagramChannel.class)
.option(UnixChannelOption.SO_REUSEPORT, true);
udpBootstrap.channel(KQueueDatagramChannel.class);
}
else {
udpBootstrap.channel(NioDatagramChannel.class);

View File

@ -0,0 +1,144 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network;
import java.io.File;
import java.lang.reflect.Field;
import dorkbox.util.NativeLoader;
import dorkbox.util.OS;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.kqueue.KQueue;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
/**
*
*/
public
class NativeLibrary {
/**
* Tries to extract the native transport libraries for Linux/MacOsX into a "semi-permanent" location. If this is unsuccessful for any
* reason, netty will fall back to it's own logic.
*/
static {
if (OS.isLinux() || OS.isMacOsX()) {
// try to load the native libraries for Linux/MacOsX...
String originalLibraryPath = SystemPropertyUtil.get("java.library.path");
File outputDirectory;
String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
if (workdir != null) {
File f = new File(workdir);
if (!f.isDirectory()) {
f.mkdirs();
}
try {
f = f.getAbsoluteFile();
} catch (Exception ignored) {
// Good to have an absolute path, but it's OK.
}
outputDirectory = f;
// logger.debug("-Dio.netty.native.workdir: " + WORKDIR);
}
else {
outputDirectory = PlatformDependent.tmpdir();
// logger.debug("-Dio.netty.native.workdir: " + WORKDIR + " (io.netty.tmpdir)");
}
try {
System.setProperty("java.library.path", originalLibraryPath + File.pathSeparator + outputDirectory.getAbsolutePath());
// reset the classloader library path.
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (Exception ignored) {
}
String staticLibName;
if (OS.isLinux()) {
staticLibName = "netty_transport_native_epoll";
}
else {
staticLibName = "netty_transport_native_kqueue";
}
staticLibName = "lib" + staticLibName + '_' + PlatformDependent.normalizedArch();
String jarLibName = "META-INF/native/" + staticLibName;
if (OS.isLinux()) {
jarLibName += ".so";
}
else {
jarLibName += ".jnilib";
}
try {
NativeLoader.extractLibrary(jarLibName, outputDirectory.getAbsolutePath(), staticLibName, null);
// we have to try to load the native library HERE, while the java.library.path has it
if (OS.isLinux()) {
//noinspection ResultOfMethodCallIgnored
Epoll.isAvailable();
}
else if (OS.isMacOsX()) {
//noinspection ResultOfMethodCallIgnored
KQueue.isAvailable();
}
} catch (Exception ignored) {
} finally {
System.setProperty("java.library.path", originalLibraryPath);
try {
// reset the classloader library path.
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (Exception ignored) {
}
}
}
// either not Linux/MacOsX, or loading the library failed.
}
/**
* @return true if the (possibly) required native libraries have been loaded
*/
public static
boolean isAvailable() {
if (OS.isLinux()) {
return Epoll.isAvailable();
}
else if (OS.isMacOsX()) {
return KQueue.isAvailable();
}
// not Linux/MacOsX
return true;
}
}

View File

@ -46,7 +46,6 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import io.netty.channel.unix.UnixChannelOption;
/**
* The server can only be accessed in an ASYNC manner. This means that the server can only be used in RESPONSE to events. If you access the
@ -151,15 +150,15 @@ class Server<C extends Connection> extends EndPointServer {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new OioEventLoopGroup(0, new NamedThreadFactory(threadName, threadGroup));
boss = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new OioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
}
else if (OS.isMacOsX()) {
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
boss = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss", threadGroup));
worker = new KQueueEventLoopGroup(EndPoint.DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName, threadGroup));
@ -200,12 +199,12 @@ class Server<C extends Connection> extends EndPointServer {
// android ONLY supports OIO (not NIO)
tcpBootstrap.channel(OioServerSocketChannel.class);
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
tcpBootstrap.channel(EpollServerSocketChannel.class);
}
else if (OS.isMacOsX()) {
// JNI network stack is MUCH faster (but only on macosx)
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
tcpBootstrap.channel(KQueueServerSocketChannel.class);
}
else {
@ -243,18 +242,15 @@ class Server<C extends Connection> extends EndPointServer {
if (udpBootstrap != null) {
if (OS.isAndroid()) {
// android ONLY supports OIO (not NIO)
udpBootstrap.channel(OioDatagramChannel.class)
.option(UnixChannelOption.SO_REUSEPORT, true);
udpBootstrap.channel(OioDatagramChannel.class);
}
else if (OS.isLinux()) {
else if (OS.isLinux() && NativeLibrary.isAvailable()) {
// JNI network stack is MUCH faster (but only on linux)
udpBootstrap.channel(EpollDatagramChannel.class)
.option(UnixChannelOption.SO_REUSEPORT, true);
udpBootstrap.channel(EpollDatagramChannel.class);
}
else if (OS.isMacOsX()) {
// JNI network stack is MUCH faster (but only on macosx)
udpBootstrap.channel(KQueueDatagramChannel.class)
.option(UnixChannelOption.SO_REUSEPORT, true);
else if (OS.isMacOsX() && NativeLibrary.isAvailable()) {
// KQueue network stack is MUCH faster (but only on macosx)
udpBootstrap.channel(KQueueDatagramChannel.class);
}
else {
// windows