All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ml.karmaconfigs.remote.messaging.worker.tcp.TCPServer Maven / Gradle / Ivy

Go to download

This API helps you to send messages between a server and a client. It's supposed to work with computers out of the same network, but I haven't tested it.

The newest version!
package ml.karmaconfigs.remote.messaging.worker.tcp;

/*
 * GNU LESSER GENERAL PUBLIC LICENSE
 * Version 2.1, February 1999
 * 

* Copyright (C) 1991, 1999 Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. *

* [This is the first released version of the Lesser GPL. It also counts * as the successor of the GNU Library Public License, version 2, hence * the version number 2.1.] */ import ml.karmaconfigs.api.common.Console; import ml.karmaconfigs.api.common.timer.scheduler.LateScheduler; import ml.karmaconfigs.api.common.timer.scheduler.worker.AsyncLateScheduler; import ml.karmaconfigs.api.common.utils.ConcurrentList; import ml.karmaconfigs.api.common.utils.PrefixConsoleData; import ml.karmaconfigs.api.common.utils.enums.Level; import ml.karmaconfigs.api.common.utils.file.PathUtilities; import ml.karmaconfigs.api.common.utils.string.StringUtils; import ml.karmaconfigs.remote.messaging.platform.Server; import ml.karmaconfigs.remote.messaging.listener.RemoteListener; import ml.karmaconfigs.remote.messaging.listener.event.server.ClientCommandEvent; import ml.karmaconfigs.remote.messaging.listener.event.server.ClientConnectEvent; import ml.karmaconfigs.remote.messaging.listener.event.server.ClientDisconnectEvent; import ml.karmaconfigs.remote.messaging.listener.event.server.ClientMessageEvent; import ml.karmaconfigs.remote.messaging.remote.RemoteClient; import ml.karmaconfigs.remote.messaging.util.DisconnectReason; import ml.karmaconfigs.remote.messaging.util.WorkLevel; import ml.karmaconfigs.remote.messaging.util.message.*; import ml.karmaconfigs.remote.messaging.worker.tcp.remote.TCPRemoteClient; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * Remote message client interface */ public final class TCPServer extends Server { private final List queue = new ConcurrentList<>(); private final Map clients = new ConcurrentHashMap<>(); private final Set banned = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set connections = Collections.newSetFromMap(new ConcurrentHashMap<>()); private String server = "127.0.0.1"; private int sv_port = 49305; private boolean debug = false; private boolean operative = false; private ServerSocketChannel socket; private int processed = 0; private String key = ""; private final Console console = new Console(this); /** * Initialize a default client that * will connect to local server at * default port 49305 */ public TCPServer() { PrefixConsoleData data = console.getData(); data.setOkPrefix("&3[TCP Server (&aOK&3)]&b "); data.setInfoPrefix("&3[TCP Server (&7INFO&3)]&b "); data.setWarnPrefix("&3[TCP Server (&eWARNING&3)]&b "); data.setGravePrefix("&3[TCP Server (&4ERROR&3)]&b "); } /** * Initialize a client with a custom port * that will connect to the specified server at the * specified port * * @param port the server port */ public TCPServer(final int port) { sv_port = port; PrefixConsoleData data = console.getData(); data.setOkPrefix("&3[TCP Server (&aOK&3)]&b "); data.setInfoPrefix("&3[TCP Server (&7INFO&3)]&b "); data.setWarnPrefix("&3[TCP Server (&eWARNING&3)]&b "); data.setGravePrefix("&3[TCP Server (&4ERROR&3)]&b "); } /** * Initialize a client that will connect * to the specified server at specified port * * @param host the server * @param port the server port */ public TCPServer(final String host, final int port) { server = host; sv_port = port; PrefixConsoleData data = console.getData(); data.setOkPrefix("&3[TCP Server (&aOK&3)]&b "); data.setInfoPrefix("&3[TCP Server (&7INFO&3)]&b "); data.setWarnPrefix("&3[TCP Server (&eWARNING&3)]&b "); data.setGravePrefix("&3[TCP Server (&4ERROR&3)]&b "); } /** * Set the server debug status * * @param status the server debug status * @return this instance */ @Override public Server debug(final boolean status) { debug = status; return this; } /** * Try to start the server * * @return a completable future when the server starts */ @Override public LateScheduler start() { if (!operative) { LateScheduler result = new AsyncLateScheduler<>(); Thread thread = new Thread(() -> { try { socket = ServerSocketChannel.open().bind(new InetSocketAddress(server, sv_port)); socket.configureBlocking(false); result.complete(true); operative = true; while (operative) { try { SocketChannel channel = socket.accept(); if (channel != null) { new Thread(() -> { while (channel.isConnected()) { try { //Allocate 512 of memory ByteBuffer tmpBuffer = ByteBuffer.allocate(5120); channel.read(tmpBuffer); InetAddress incoming = ((InetSocketAddress) channel.getRemoteAddress()).getAddress(); int port = ((InetSocketAddress) channel.getRemoteAddress()).getPort(); String default_name = incoming.getHostAddress() + "/" + port; ByteBuffer BUFFER = DataFixer.fixBuffer(tmpBuffer); MessageInput input = new MessageDataInput(BUFFER.array()); if (!queue.isEmpty()) { queue.add(input); input = queue.get(0); } String mac = input.getString("MAC"); RemoteClient client = getClient(default_name, mac, incoming, port, channel); if (input.getBoolean("COMMAND_ENABLED")) { String command = input.getString("COMMAND"); String argument = input.getString("ARGUMENT"); if (command != null && argument != null) { switch (command.toLowerCase()) { case "connect": boolean sendDecline = true; boolean validKey = true; if (!banned.contains(mac)) { if (!StringUtils.isNullOrEmpty(key)) { String provided = input.getString("ACCESS_KEY"); if (provided != null) { //Basically, an 'access' password validKey = provided.equals(key); } else { validKey = false; } } if (validKey) { sendDecline = false; if (debug) { console.send("Client {0} connected as {1}", Level.OK, default_name, argument); } client = new TCPRemoteClient(argument, mac, incoming, port, channel); clients.put(default_name, client); connections.add(default_name); ClientConnectEvent event = new ClientConnectEvent(client, this); RemoteListener.callServerEvent(event); MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "accept"); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } } if (sendDecline) { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "decline"); output.write("ARGUMENT", "connect"); output.write("COMMAND_ARGUMENT", (validKey ? "You are banned from this server!" : "The provided access key is not valid for this server!")); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } break; case "rename": if (connections.contains(default_name)) { if (debug) { console.send("Client {0} is now known as {1}", Level.WARNING, client.getName(), argument); } client = new TCPRemoteClient(argument, mac, incoming, port, channel); clients.put(default_name, client); ClientCommandEvent event = new ClientCommandEvent(client, this, command, argument); RemoteListener.callServerEvent(event); MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "success"); output.write("ARGUMENT", "rename"); output.write("ARGUMENT_DATA", argument); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } else { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "failed"); output.write("ARGUMENT", "rename"); output.write("ARGUMENT_DATA", argument + "," + "You are not connected to this server!"); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } break; case "disconnect": if (connections.contains(default_name)) { if (debug) { console.send("Client {0} left the server ( {1} )", Level.WARNING, client.getName(), argument); } clients.remove(default_name); connections.remove(default_name); ClientDisconnectEvent event = new ClientDisconnectEvent(client, this, DisconnectReason.KILLED_BY_CLIENT, argument); RemoteListener.callServerEvent(event); } else { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "failed"); output.write("ARGUMENT", "disconnect"); output.write("ARGUMENT_DATA", "You are not connected to this server!"); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } break; default: if (connections.contains(default_name)) { if (debug) { console.send("Unknown command from {0}: {1} ( {2} )", Level.WARNING, client.getName(), command, argument); } ClientCommandEvent event = new ClientCommandEvent(client, this, command, argument); RemoteListener.callServerEvent(event); } else { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "failed"); output.write("ARGUMENT", "unknown"); output.write("ARGUMENT_DATA", command + "," + argument + "," + "You are not connected to this server!"); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } break; } } } else { if (connections.contains(default_name)) { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "success"); output.write("ARGUMENT", "message"); output.write("ARGUMENT_DATA", client.getName()); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); ClientMessageEvent event = new ClientMessageEvent(client, this, input); RemoteListener.callServerEvent(event); } else { if (debug) { console.send("Denying message from {0} because he's not connected to server", Level.INFO, default_name); } MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "failed"); output.write("ARGUMENT", "message"); output.write("ARGUMENT_DATA", "You are not connected to this server!"); byte[] compile = output.compile(); ByteBuffer writeBuffer = ByteBuffer.wrap(compile); channel.write(writeBuffer); } } } catch (Throwable ex) { ex.printStackTrace(); } processed++; } }).start(); } } catch (Throwable ex) { ex.printStackTrace(); System.exit(1); } } } catch (Throwable ex) { result.complete(false); } }); thread.start(); return result; } return null; } /** * Try to start the server * * @param accessKey the server access key * @return a completable future when the server starts */ @Override public LateScheduler start(final String accessKey) { if (!operative) { key = accessKey; return start(); } return null; } /** * Get the server address * * @return the server address */ @Override public InetAddress getHost() { try { try { return InetAddress.getByName(server); } catch (Throwable ignored) {} return InetAddress.getLocalHost(); } catch (Throwable ex) { return InetAddress.getLoopbackAddress(); } } /** * Get the server MAC address * * @return the server MAC address */ @Override public String getMAC() { try { NetworkInterface network = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()); byte[] macArray = network.getHardwareAddress(); StringBuilder str = new StringBuilder(); for (int i = 0; i < macArray.length; i++) { str.append(String.format("%02X%s", macArray[i], (i < macArray.length - 1) ? ":" : "")); } return str.toString(); } catch (Throwable ex) { System.out.println("Failed to locate MAC address..."); System.exit(1); return null; } } /** * Get the server port * * @return the server port */ @Override public int getPort() { return sv_port; } /** * Send a message to the server * * @param message the message to send * @return if the message could be sent */ @Override public boolean sendMessage(byte[] message) { return false; } /** * Get the connected clients * * @return a list of connected clients */ @Override public Set getClients() { return new HashSet<>(clients.values()); } /** * Get the client work level * * @return the client work level */ @Override public WorkLevel getWorkLevel() { return WorkLevel.TCP; } /** * Close the connection */ @Override public void close() { operative = false; try { socket.close(); } catch (Throwable ex) { ex.printStackTrace(); } } /** * Export the list of bans * * @param destination the file were to store * the ban list */ @Override public void exportBans(final Path destination) { try { PathUtilities.create(destination); String serialized = StringUtils.serialize(new ArrayList<>(banned)); Files.write(destination, serialized.getBytes(), StandardOpenOption.CREATE); } catch (Throwable ex) { ex.printStackTrace(); } } /** * Load the list of bans * * @param bans the file were the banned mac * addresses are stored */ @Override public void loadBans(final Path bans) { try { PathUtilities.create(bans); byte[] result = Files.readAllBytes(bans); Object serialized = StringUtils.load(new String(result)); if (serialized instanceof ArrayList) { ArrayList list = (ArrayList) serialized; for (Object obj : list) ban(String.valueOf(obj)); } } catch (Throwable ex) { ex.printStackTrace(); } } /** * Send a message to each connected client * * @param data the data to send */ @Override public void broadcast(final MessageOutput data) { int max = queue.size(); int expected = processed + max; new Thread(() -> { long wait = 0; //Before doing anything, we must make sure the queue is not busy, if that's the case //the server will keep this thread busy until it can proceed, based on the moment max tasks //and on the moment executed tasks while (processed < expected) { wait++; } if (debug) { long sec = TimeUnit.MILLISECONDS.toSeconds(wait); console.send("Sent message to everyone after {0} {1}", Level.OK, (sec > 0 ? sec : wait), (sec > 0 ? "seconds" : "ms")); } for (RemoteClient client : clients.values()) { client.sendMessage(data); } }).start(); } /** * Redirect a message to the specified client * * @param name the client name * @param data the message */ @Override public void redirect(final String name, final MessageOutput data) { int max = queue.size(); int expected = processed + max; new Thread(() -> { long wait = 0; //Before doing anything, we must make sure the queue is not busy, if that's the case //the server will keep this thread busy until it can proceed, based on the moment max tasks //and on the moment executed tasks while (processed < expected) { wait++; } if (debug) { long sec = TimeUnit.MILLISECONDS.toSeconds(wait); console.send("Sent message to {0} after {1} {2}", Level.OK, name, (sec > 0 ? sec : wait), (sec > 0 ? "seconds" : "ms")); } for (RemoteClient client : clients.values()) { if (client.getName().equals(name) || client.getMAC().equals(name)) { client.sendMessage(data); } } }).start(); /* Old method, efficient but had some problems if (queue.isEmpty()) { for (RemoteClient client : clients.values()) { System.out.println(client.getName() + "/" + client.getMAC()); if (client.getName().equals(name) || client.getMAC().equals(name)) { client.sendMessage(data); } } }*/ } /** * Ban an address from the server * * @param macAddresses the addresses to ban */ @Override public void ban(final String... macAddresses) { banned.addAll(Arrays.asList(macAddresses)); for (RemoteClient client : clients.values()) { if (banned.contains(client.getMAC())) { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "DISCONNECT"); output.write("ARGUMENT", ""); output.write("ARGUMENT_DATA", "You have been banned from this server!"); client.sendMessage(output); } } } /** * Kick an address from the server * * @param macAddresses the addresses to kick */ @Override public void kick(final String... macAddresses) { List macs = Arrays.asList(macAddresses); for (RemoteClient client : clients.values()) { if (macs.contains(client.getMAC())) { MessageOutput output = new MessageDataOutput(); output.write("MAC", getMAC()); output.write("COMMAND_ENABLED", true); output.write("COMMAND", "DISCONNECT"); output.write("ARGUMENT", ""); output.write("ARGUMENT_DATA", "You have been kicked from this server!"); client.sendMessage(output); } } } /** * Unban an address from the server * * @param macAddresses the addresses to unban */ @Override public void unBan(final String... macAddresses) { Arrays.asList(macAddresses).forEach(banned::remove); } /** * Get the client * * @param name the client name * @param address the client address * @param port the client port * @param socket the client socket * @return the client */ private RemoteClient getClient(final String name, final String mac, final InetAddress address, final int port, final SocketChannel socket) { RemoteClient client = clients.getOrDefault(address.getHostAddress() + "/" + port, null); if (client == null) { client = new TCPRemoteClient(name, mac, address, port, socket); clients.put(address.getHostAddress() + "/" + port, client); } return client; } /** * Karma source name * * @return the source name */ @Override public String name() { return "TCP Server"; } /** * Karma source version * * @return the source version */ @Override public String version() { return "0"; } /** * Karma source description * * @return the source description */ @Override public String description() { return "TCP server to allow TCP Clients from RemoteMessaging API connect"; } /** * Karma source authors * * @return the source authors */ @Override public String[] authors() { return new String[]{"KarmaDev"}; } /** * Karma source update URL * * @return the source update URL */ @Override public String updateURL() { return null; } /** * Get the source out * * @return the source out */ @Override public Console console() { return console; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy