/* * Copyright 2023 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 dorkboxTest.network import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger import ch.qos.logback.classic.encoder.PatternLayoutEncoder import ch.qos.logback.classic.joran.JoranConfigurator import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.ConsoleAppender import dorkbox.netUtil.IPv4 import dorkbox.network.Client import dorkbox.network.ClientConfiguration import dorkbox.network.Server import dorkbox.network.ServerConfiguration import dorkbox.network.connection.Connection import dorkbox.network.ipFilter.IpSubnetFilterRule import dorkbox.storage.Storage import org.slf4j.LoggerFactory import sun.misc.Unsafe import java.lang.reflect.Field /** * */ @Suppress("UNUSED_ANONYMOUS_PARAMETER") class AeronClientServer { companion object { init { try { val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe") theUnsafe.isAccessible = true val u = theUnsafe.get(null) as Unsafe val cls = Class.forName("jdk.internal.module.IllegalAccessLogger") val logger: Field = cls.getDeclaredField("logger") u.putObjectVolatile(cls, u.staticFieldOffset(logger), null) } catch (e: NoSuchFieldException) { e.printStackTrace() } catch (e: IllegalAccessException) { e.printStackTrace() } catch (e: ClassNotFoundException) { e.printStackTrace() } // assume SLF4J is bound to logback in the current environment val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger val context = rootLogger.loggerContext val jc = JoranConfigurator() jc.context = context context.reset() // override default configuration // rootLogger.setLevel(Level.OFF); // rootLogger.setLevel(Level.INFO); // rootLogger.level = Level.DEBUG rootLogger.level = Level.TRACE // rootLogger.setLevel(Level.ALL); // we only want error messages val nettyLogger = LoggerFactory.getLogger("io.netty") as Logger nettyLogger.level = Level.ERROR // we only want error messages val kryoLogger = LoggerFactory.getLogger("com.esotericsoftware") as Logger kryoLogger.level = Level.ERROR val encoder = PatternLayoutEncoder() encoder.context = context encoder.pattern = "%date{HH:mm:ss.SSS} %-5level [%logger{35}] %msg%n" encoder.start() val consoleAppender = ConsoleAppender() consoleAppender.context = context consoleAppender.encoder = encoder consoleAppender.start() rootLogger.addAppender(consoleAppender) } /** * Command-line entry point. * * @param args Command-line arguments * * @throws Exception On any error */ @Throws(Exception::class) @JvmStatic fun main(args: Array) { val acs = AeronClientServer() if (args.contains("client")) { acs.client("172.31.79.129") } else if (args.contains("server")) { val server = acs.server() server.waitForClose() } else { acs.server() acs.client("localhost") } } } fun client(remoteAddress: String) { val configuration = ClientConfiguration() configuration.settingsStore = Storage.Memory() // don't want to persist anything on disk! configuration.appId = "aeron_test" configuration.enableIpc = false // configuration.enableIPv4 = false configuration.enableIPv6 = false // configuration.uniqueAeronDirectory = true val client = Client(configuration) client.onInit { logger.error("initialized") } client.onConnect { logger.error("connected") send("HI THERE!") } client.onDisconnect { logger.error("disconnect") } client.onError { throwable -> logger.error("has error") throwable.printStackTrace() } client.onMessage { message -> logger.error("HAS MESSAGE! $message") } client.connect(remoteAddress, 2000) // UDP connection via loopback // different ones needed // send - reliable // send - unreliable // send - priority (0-255 -- 255 is MAX priority) when sending, max is always sent immediately, then lower priority is sent if there is no backpressure from the MediaDriver. // send - IPC/local // runBlocking { // while (!client.isShutdown()) { // client.send("ECHO " + java.lang.Long.toUnsignedString(client.crypto.secureRandom.nextLong(), 16)) // } // } // connection needs to know // is UDP or IPC // host address // RMI // client.get(5) -> gets from the server connection, if exists, then global. // on server, a connection local RMI object "uses" an id for global, so there will never be a conflict // using some tricks, we can make it so that it DOESN'T matter the order in which objects are created, // and can specify, if we want, the object created. // Once created though, as NEW ONE with the same ID cannot be created until the old one is removed! Thread.sleep(2000L) client.close() } fun server(): Server { val configuration = ServerConfiguration() configuration.settingsStore = Storage.Memory() // don't want to persist anything on disk! configuration.listenIpAddress = "*" configuration.maxClientCount = 50 configuration.appId = "aeron_test" configuration.enableIpc = false // configuration.enableIPv4 = false configuration.enableIPv6 = false configuration.maxConnectionsPerIpAddress = 50 val server = Server(configuration) // we must always make sure that aeron is shut-down before starting again. if (!server.ensureStopped()) { throw IllegalStateException("Aeron was unable to shut down in a timely manner.") } server.filter(IpSubnetFilterRule(IPv4.LOCALHOST, 32)) server.filter { println("should the connection $this be allowed?") true } server.onInit { logger.error("initialized") } server.onConnect { logger.error("connected: $this") } server.onDisconnect { logger.error("disconnect: $this") } server.onErrorGlobal { throwable -> server.logger.error("from test: has error") throwable.printStackTrace() } server.onError { throwable -> logger.error("from test: has connection error: $this") throwable.printStackTrace() } server.onMessage { message -> logger.error("got message! $message") send("ECHO $message") } server.bind(2000) return server } }