- Very big NetworkSystem updates

This commit is contained in:
2025-09-20 12:38:58 +02:00
parent d33b556578
commit b768fdfc40
7 changed files with 419 additions and 446 deletions

View File

@@ -1,11 +1,3 @@
/*
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
* See LICENSE-File if exists
*/
package me.finn.unlegitlibrary.network.system.client;
import me.finn.unlegitlibrary.event.EventManager;
@@ -13,15 +5,16 @@ import me.finn.unlegitlibrary.network.system.client.events.*;
import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
import me.finn.unlegitlibrary.network.utils.PemUtils;
import me.finn.unlegitlibrary.utils.DefaultMethodsOverrider;
import me.finn.unlegitlibrary.utils.Logger;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.net.ssl.*;
import java.io.*;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.net.Proxy;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
public final class NetworkClient {
private final String host;
@@ -29,50 +22,33 @@ public final class NetworkClient {
private final PacketHandler packetHandler;
private final EventManager eventManager;
private final Logger logger;
private final int maxReconnectAttempts;
private final int reconnectDelay;
private Socket socket;
private final int timeout;
private SSLSocket socket;
private ObjectOutputStream outputStream;
private ObjectInputStream inputStream;
private int clientID;
private int currentAttempts;
private NetworkClient(String host, int port, PacketHandler packetHandler, EventManager eventManager, Logger logger, int reconnectAttempts, int reconnectDelay, int timeout) {
this.host = host;
this.port = port;
this.clientID = -1;
this.timeout = timeout;
private final SSLSocketFactory sslSocketFactory;
private final SSLParameters sslParameters;
this.packetHandler = packetHandler;
this.eventManager = eventManager;
this.logger = logger;
private final Thread receiveThread = new Thread(this::receive);
this.maxReconnectAttempts = reconnectAttempts;
this.reconnectDelay = reconnectDelay;
this.currentAttempts = 0;
this.packetHandler.setClientInstance(this);
this.packetHandler.registerPacket(new ClientIDPacket());
}
public int getClientID() {
return clientID;
} private final Thread receiveThread = new Thread(this::receive);
public void setClientID(int clientID) {
if (this.clientID == -1) this.clientID = clientID;
}
public Socket getSocket() {
return socket;
public PacketHandler getPacketHandler() {
return packetHandler;
}
public EventManager getEventManager() {
return eventManager;
}
public PacketHandler getPacketHandler() {
return packetHandler;
public ObjectInputStream getInputStream() {
return inputStream;
}
public SSLSocket getSocket() {
return socket;
}
public ObjectOutputStream getOutputStream() {
return outputStream;
}
public Logger getLogger() {
@@ -87,200 +63,188 @@ public final class NetworkClient {
return host;
}
public boolean isConnected() {
return socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
private NetworkClient(String host, int port, PacketHandler packetHandler,
EventManager eventManager, Logger logger,
int timeout, SSLSocketFactory sslSocketFactory,
SSLParameters sslParameters) {
this.host = host;
this.port = port;
this.packetHandler = packetHandler;
this.eventManager = eventManager;
this.logger = logger;
this.timeout = timeout;
this.sslSocketFactory = sslSocketFactory;
this.sslParameters = sslParameters;
this.packetHandler.setClientInstance(this);
this.packetHandler.registerPacket(new ClientIDPacket());
}
public boolean isAutoReconnectEnabled() {
return maxReconnectAttempts != 0;
public boolean isConnected() {
return socket != null && socket.isConnected() && !socket.isClosed()
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
}
public synchronized boolean connect() throws ConnectException {
if (isConnected()) return false;
if (logger == null) System.out.println("Trying to connect to " + host + ":" + port + "...");
else logger.info("Trying to connect to " + host + ":" + port + "...");
if (logger != null) logger.info("Trying to connect to " + host + ":" + port + "...");
else System.out.println("Trying to connect to " + host + ":" + port + "...");
try {
socket = new Socket(host, port);
if (sslSocketFactory == null) throw new ConnectException("SSL socket factory not set. Client certificate required!");
socket = (SSLSocket) sslSocketFactory.createSocket(host, port);
if (sslParameters != null) socket.setSSLParameters(sslParameters);
else {
SSLParameters defaultParams = socket.getSSLParameters();
defaultParams.setProtocols(new String[]{"TLSv1.3"});
socket.setSSLParameters(defaultParams);
}
socket.setTcpNoDelay(true);
socket.setSoTimeout(timeout);
try {
socket.startHandshake();
} catch (Exception handshakeEx) {
throw new ConnectException("Handshake failed: " + handshakeEx.getMessage());
}
outputStream = new ObjectOutputStream(socket.getOutputStream());
inputStream = new ObjectInputStream(socket.getInputStream());
receiveThread.start();
if (currentAttempts == 0) currentAttempts++;
if (logger == null)
System.out.println("Connected to " + host + ":" + port + " (Attempts: " + currentAttempts + ")");
else logger.info("Connected to " + host + ":" + port + " (Attempts: " + currentAttempts + ")");
eventManager.executeEvent(new ClientConnectedEvent(this));
currentAttempts = 0;
if (logger != null) logger.info("Connected to " + host + ":" + port);
else System.out.println("Connected to " + host + ":" + port);
return true;
} catch (IOException exception) {
if (isAutoReconnectEnabled()) {
try {
Thread.sleep(reconnectDelay);
} catch (InterruptedException sleepThreadException) {
if (logger == null) System.err.println("Reconnect exception: " + sleepThreadException.getMessage());
else logger.exception("Reconnect exception", sleepThreadException);
}
currentAttempts++;
if (currentAttempts < maxReconnectAttempts || maxReconnectAttempts < 0) return connect();
}
} catch (Exception e) {
throw new ConnectException("Failed to connect: " + e.getMessage());
}
throw new ConnectException("Failed to connect to " + host + ":" + port);
}
private void receive() {
if (!isConnected()) return;
while (isConnected()) {
try {
try {
while (isConnected()) {
Object received = inputStream.readObject();
if (received instanceof Integer) {
int packetID = (Integer) received;
if (packetHandler.isPacketIDRegistered(packetID)) {
Packet packet = packetHandler.getPacketByID(packetID);
if (packetHandler.handlePacket(packetID, packet, inputStream))
eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
else
eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
} catch (SocketException ignored) {
try {
disconnect();
} catch (ConnectException exception) {
exception.printStackTrace();
}
} catch (Exception exception) {
exception.printStackTrace();
return;
handleReceived(received);
}
} catch (Exception e) { disconnect(); }
}
private void handleReceived(Object received) throws IOException, ClassNotFoundException {
if (received instanceof Integer id) {
if (packetHandler.isPacketIDRegistered(id)) {
Packet packet = packetHandler.getPacketByID(id);
boolean handled = packetHandler.handlePacket(id, packet, inputStream);
if (handled) eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
else eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
}
public synchronized boolean disconnect() {
if (!isConnected()) return false;
try {
receiveThread.interrupt();
if (outputStream != null) outputStream.close();
if (inputStream != null) inputStream.close();
if (socket != null) socket.close();
} catch (IOException e) {
if (logger != null) logger.exception("Error closing connection", e);
else System.err.println("Error closing connection: " + e.getMessage());
} finally {
socket = null;
outputStream = null;
inputStream = null;
eventManager.executeEvent(new ClientDisconnectedEvent(this));
}
return true;
}
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
if (!isConnected()) return false;
try {
if (packetHandler.sendPacket(packet, outputStream)) {
eventManager.executeEvent(new C_PacketSendEvent(this, packet));
return true;
} else {
eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
return false;
}
} catch (IOException | ClassNotFoundException exception) {
throw exception;
}
}
public synchronized boolean disconnect() throws ConnectException {
boolean wasConnected = isConnected();
if (wasConnected) {
if (logger == null) System.out.println("Disconnecting from server...");
else logger.info("Disconnecting from server...");
}
if (receiveThread.isAlive() && !receiveThread.isInterrupted()) receiveThread.interrupt();
if (wasConnected) {
try {
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException exception) {
if (logger == null) System.err.println("Failed to close socket: " + exception.getMessage());
else logger.exception("Failed to close socket", exception);
}
}
outputStream = null;
inputStream = null;
socket = null;
currentAttempts = 0;
if (wasConnected) {
if (logger == null) System.out.println("Disconnected from server");
else logger.info("Disconnected from server");
}
eventManager.executeEvent(new ClientDisconnectedEvent(this));
clientID = -1;
if (isAutoReconnectEnabled() && (currentAttempts < maxReconnectAttempts || maxReconnectAttempts < 0))
return connect();
return true;
boolean sent = packetHandler.sendPacket(packet, outputStream);
if (sent) eventManager.executeEvent(new C_PacketSendEvent(this, packet));
else eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
return sent;
}
// --- Builder ---
public static class ClientBuilder extends DefaultMethodsOverrider {
private String host;
private int port;
private PacketHandler packetHandler;
private EventManager eventManager;
private Logger logger;
private int timeout = 5000;
private SSLSocketFactory sslSocketFactory;
private SSLParameters sslParameters;
private File caFolder;
private File clientFolder;
private File keyFolder;
private int maxReconnectAttempts = 0;
private int reconnectDelay = 3000;
private int timeout = 0;
public ClientBuilder setHost(String host) { this.host = host; return this; }
public ClientBuilder setPort(int port) { this.port = port; return this; }
public ClientBuilder setPacketHandler(PacketHandler handler) { this.packetHandler = handler; return this; }
public ClientBuilder setEventManager(EventManager manager) { this.eventManager = manager; return this; }
public ClientBuilder setLogger(Logger logger) { this.logger = logger; return this; }
public ClientBuilder setTimeout(int timeout) { this.timeout = timeout; return this; }
public ClientBuilder setSSLSocketFactory(SSLSocketFactory factory) { this.sslSocketFactory = factory; return this; }
public ClientBuilder setSSLParameters(SSLParameters params) { this.sslParameters = params; return this; }
public ClientBuilder setRootCAFolder(File folder) { this.caFolder = folder; return this; }
public ClientBuilder setClientCertificatesFolder(File clientFolder, File keyFolder) { this.clientFolder = clientFolder; this.keyFolder = keyFolder; return this; }
public final NetworkClient build() {
return new NetworkClient(host, port, packetHandler, eventManager, logger, maxReconnectAttempts, reconnectDelay, timeout);
public NetworkClient build() {
if (sslSocketFactory == null && caFolder != null) {
try { sslSocketFactory = createSSLSocketFactory(caFolder, clientFolder, keyFolder); }
catch (Exception e) { throw new RuntimeException("Failed to create SSLFactory", e); }
}
return new NetworkClient(host, port, packetHandler, eventManager, logger,
timeout, sslSocketFactory, sslParameters);
}
public final ClientBuilder setEventManager(EventManager eventManager) {
this.eventManager = eventManager;
return this;
}
public static SSLSocketFactory createSSLSocketFactory(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
// TrustStore (Root-CAs)
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
public final ClientBuilder setHost(String host) {
this.host = host;
return this;
}
int caIndex = 1;
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
try (FileInputStream fis = new FileInputStream(caFile)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate caCert = cf.generateCertificate(fis);
trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
}
}
public final ClientBuilder setLogger(Logger logger) {
this.logger = logger;
return this;
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
public final ClientBuilder setMaxReconnectAttempts(int maxReconnectAttempts) {
this.maxReconnectAttempts = maxReconnectAttempts;
return this;
}
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null); // Kein Passwort nötig
public final ClientBuilder setPacketHandler(PacketHandler packetHandler) {
this.packetHandler = packetHandler;
return this;
}
int clientIndex = 1;
for (File certFile : clientCertFolder.listFiles((f) -> f.getName().endsWith(".crt"))) {
String baseName = certFile.getName().replace(".crt", "");
File keyFile = new File(clientKeyFolder, baseName + ".key");
if (!keyFile.exists()) continue;
public final ClientBuilder setPort(int port) {
this.port = port;
return this;
}
java.security.PrivateKey key = PemUtils.loadPrivateKey(keyFile);
java.security.cert.Certificate cert = PemUtils.loadCertificate(certFile);
public final ClientBuilder setReconnectDelay(int reconnectDelay) {
this.reconnectDelay = reconnectDelay;
return this;
}
keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
}
public final ClientBuilder setTimeout(int timeout) {
this.timeout = timeout;
return this;
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, null);
// SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
}
}

View File

@@ -1,35 +1,42 @@
/*
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
* See LICENSE-File if exists
*/
package me.finn.unlegitlibrary.network.system.server;
import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
import me.finn.unlegitlibrary.network.system.server.events.*;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.security.cert.X509Certificate;
public class ConnectionHandler {
public final NetworkServer networkServer;
private Socket socket;
private SSLSocket socket;
private int clientID;
private ObjectOutputStream outputStream;
private ObjectInputStream inputStream;
public ConnectionHandler(NetworkServer server, Socket socket, int clientID) throws IOException, ClassNotFoundException {
this.networkServer = server;
this.socket = socket;
this.clientID = clientID;
private final NetworkServer server;
public SSLSocket getSocket() {
return socket;
}
public ObjectOutputStream getOutputStream() {
return outputStream;
}
public ObjectInputStream getInputStream() {
return inputStream;
}
public NetworkServer getServer() {
return server;
}
public ConnectionHandler(NetworkServer server, SSLSocket socket, int clientID) throws IOException, ClassNotFoundException {
this.server = server; this.socket = socket; this.clientID = clientID;
outputStream = new ObjectOutputStream(socket.getOutputStream());
inputStream = new ObjectInputStream(socket.getInputStream());
@@ -37,97 +44,57 @@ public class ConnectionHandler {
receiveThread.start();
sendPacket(new ClientIDPacket());
networkServer.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
} public final Thread receiveThread = new Thread(this::receive);
public int getClientID() {
return clientID;
server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
}
public final boolean isConnected() {
return networkServer.isRunning() && socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
}
public final Thread receiveThread = new Thread(this::receive);
public int getClientID() { return clientID; }
public boolean isConnected() { return socket != null && socket.isConnected() && !socket.isClosed() && receiveThread.isAlive(); }
public synchronized boolean disconnect() {
boolean wasConnected = isConnected();
if (!isConnected()) return false;
if (receiveThread.isAlive()) receiveThread.interrupt();
if (wasConnected) {
if (networkServer.getLogger() == null)
System.out.println("Client ID '" + clientID + "' is disconnecting from server...");
else networkServer.getLogger().info("Client ID '" + clientID + "' is disconnecting from server...");
}
try { outputStream.close(); inputStream.close(); socket.close(); } catch (IOException ignored) {}
socket = null; outputStream = null; inputStream = null; clientID = -1;
if (receiveThread.isAlive() && !receiveThread.isInterrupted()) receiveThread.interrupt();
server.getConnectionHandlers().remove(this);
server.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this));
if (wasConnected) {
try {
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException exception) {
if (networkServer.getLogger() == null)
System.err.println("Client ID '" + clientID + "' failed to close socket: " + exception.getMessage());
else
networkServer.getLogger().exception("Client ID '" + clientID + "' failed to close socket", exception);
}
}
outputStream = null;
inputStream = null;
socket = null;
networkServer.getConnectionHandlers().remove(this);
if (wasConnected) {
if (networkServer.getLogger() == null)
System.out.println("Client ID '" + clientID + "' disconnected from server");
else networkServer.getLogger().info("Client ID '" + clientID + "' disconnected from server");
}
networkServer.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this));
clientID = -1;
return true;
}
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
if (!isConnected()) return false;
boolean sent = server.getPacketHandler().sendPacket(packet, outputStream);
if (networkServer.getPacketHandler().sendPacket(packet, outputStream)) {
networkServer.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
return true;
} else {
networkServer.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
return false;
}
if (sent) server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
else server.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
return sent;
}
private void receive() {
if (!isConnected()) return;
while (isConnected()) {
try {
Object received = inputStream.readObject();
if (received instanceof Integer) {
int packetID = (Integer) received;
if (networkServer.getPacketHandler().isPacketIDRegistered(packetID)) {
Packet packet = networkServer.getPacketHandler().getPacketByID(packetID);
if (networkServer.getPacketHandler().handlePacket(packetID, packet, inputStream))
networkServer.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
else
networkServer.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
} else
networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
} else networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
} catch (SocketException ignored) {
int id = (Integer) received;
if (server.getPacketHandler().isPacketIDRegistered(id)) {
Packet packet = server.getPacketHandler().getPacketByID(id);
if (server.getPacketHandler().handlePacket(id, packet, inputStream)) server.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
else server.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
} catch (SocketException se) { disconnect(); }
catch (Exception ex) {
if (server.getLogger() != null) server.getLogger().exception("Receive thread exception for client " + clientID, ex);
else System.err.println("Receive thread exception for client " + clientID + ": " + ex.getMessage());
disconnect();
} catch (Exception exception) {
exception.printStackTrace();
return;
}
}
}
}
}

View File

@@ -1,27 +1,21 @@
/*
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
* See LICENSE-File if exists
*/
package me.finn.unlegitlibrary.network.system.server;
import me.finn.unlegitlibrary.event.EventManager;
import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
import me.finn.unlegitlibrary.network.system.server.events.IncomingConnectionEvent;
import me.finn.unlegitlibrary.network.utils.PemUtils;
import me.finn.unlegitlibrary.utils.DefaultMethodsOverrider;
import me.finn.unlegitlibrary.utils.Logger;
import java.io.IOException;
import java.net.ServerSocket;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public final class NetworkServer {
private final int port;
@@ -29,33 +23,14 @@ public final class NetworkServer {
private final EventManager eventManager;
private final Logger logger;
private final int timeout;
private final int maxRestartAttempts;
private final int restartDelay;
private SSLServerSocket serverSocket;
private final SSLServerSocketFactory sslServerSocketFactory;
private final List<ConnectionHandler> connectionHandlers = new ArrayList<>();
private int currentAttempts;
private ServerSocket serverSocket;
private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager, Logger logger, int maxRestartAttempts, int restartDelay, int timeout) {
this.port = port;
this.timeout = timeout;
private final Thread incomingThread = new Thread(this::incomingConnections);
this.packetHandler = packetHandler;
this.eventManager = eventManager;
this.logger = logger;
this.maxRestartAttempts = maxRestartAttempts;
this.restartDelay = restartDelay;
this.currentAttempts = 0;
this.packetHandler.setServerInstance(this);
this.packetHandler.registerPacket(new ClientIDPacket());
} public final Thread incomingConnectionThread = new Thread(this::incomingConnection);
public Logger getLogger() {
return logger;
}
public EventManager getEventManager() {
return eventManager;
public List<ConnectionHandler> getConnectionHandlers() {
return connectionHandlers;
}
public int getPort() {
@@ -66,164 +41,133 @@ public final class NetworkServer {
return packetHandler;
}
public List<ConnectionHandler> getConnectionHandlers() {
return connectionHandlers;
public Logger getLogger() {
return logger;
}
public boolean isAutoRestartEnabled() {
return maxRestartAttempts != 0;
public SSLServerSocket getServerSocket() {
return serverSocket;
}
public boolean isRunning() {
return serverSocket != null && !serverSocket.isClosed() && serverSocket.isBound() &&
incomingConnectionThread.isAlive() && !incomingConnectionThread.isInterrupted();
public EventManager getEventManager() {
return eventManager;
}
public ConnectionHandler getConnectionHandlerByID(int clientID) {
for (ConnectionHandler connectionHandler : connectionHandlers)
if (connectionHandler.getClientID() == clientID) return connectionHandler;
return null;
private boolean requireClientCert;
private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager,
Logger logger, int timeout, SSLServerSocketFactory factory, boolean requireClientCert) {
this.port = port; this.packetHandler = packetHandler; this.eventManager = eventManager;
this.logger = logger; this.timeout = timeout; this.sslServerSocketFactory = factory;
this.packetHandler.setServerInstance(this);
this.packetHandler.registerPacket(new ClientIDPacket());
this.requireClientCert = requireClientCert;
}
public synchronized boolean start() {
if (isRunning()) return false;
if (logger == null) System.out.println("Trying to start on port " + port + "...");
else logger.info("Trying to start on port " + port + "...");
public boolean start() {
try {
serverSocket = new ServerSocket(port);
serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
serverSocket.setNeedClientAuth(requireClientCert);
serverSocket.setSoTimeout(timeout);
incomingConnectionThread.start();
if (currentAttempts == 0) currentAttempts++;
if (logger == null) System.out.println("Started at port " + port + " (Attempts: " + currentAttempts + ")");
else logger.info("Started at port " + port + " (Attempts: " + currentAttempts + ")");
currentAttempts = 0;
serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
incomingThread.start();
if (logger != null) logger.log("Server started on port " + port);
else System.out.println("Server started on port " + port);
return true;
} catch (IOException exception) {
if (maxRestartAttempts != 0) {
try {
Thread.sleep(restartDelay);
} catch (InterruptedException sleepThreadException) {
if (logger == null) System.err.println("Restart exception: " + sleepThreadException.getMessage());
else logger.exception("Restart exception", sleepThreadException);
}
currentAttempts++;
if (currentAttempts <= maxRestartAttempts || maxRestartAttempts < 0) return start();
}
if (logger == null) System.err.println("Failed to start on port " + port + ": " + exception.getMessage());
else logger.exception("Failed to start on port " + port, exception);
}
return false;
} catch (Exception e) {if (logger != null) logger.exception("Failed to start", e);
else System.err.println("Failed to start: " + e.getMessage()); return false; }
}
public boolean broadcastPacket(Packet packet) {
AtomicBoolean toReturn = new AtomicBoolean(false);
connectionHandlers.forEach(connectionHandler -> {
try {
if (!toReturn.get()) return;
toReturn.set(connectionHandler.sendPacket(packet));
} catch (IOException | ClassNotFoundException e) {
toReturn.set(false);
}
});
return toReturn.get();
}
private void incomingConnection() {
if (!isRunning()) return;
private void incomingConnections() {
try {
while (isRunning()) {
while (!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
socket.setTcpNoDelay(true);
socket.setSoTimeout(timeout);
if (logger == null) System.out.println("Accepted connection from " + socket.getRemoteSocketAddress());
else logger.info("Accepted connection from " + socket.getRemoteSocketAddress());
IncomingConnectionEvent incomingConnectionEvent = new IncomingConnectionEvent(this, socket);
eventManager.executeEvent(incomingConnectionEvent);
if (incomingConnectionEvent.isCancelled()) {
socket.close();
return;
if (!(socket instanceof SSLSocket ssl)) { socket.close(); continue; }
ssl.setTcpNoDelay(true);
ssl.setSoTimeout(timeout);
try { ssl.startHandshake(); }
catch (Exception handshakeEx) {
if (logger != null) logger.exception("Handshake failed", handshakeEx);
else System.err.println("Handshake failed: " + handshakeEx.getMessage());
ssl.close();
continue;
}
ConnectionHandler connectionHandler = new ConnectionHandler(this, socket, connectionHandlers.size() + 1);
connectionHandlers.add(connectionHandler);
IncomingConnectionEvent event = new IncomingConnectionEvent(this, ssl);
eventManager.executeEvent(event);
if (event.isCancelled()) { ssl.close(); continue; }
try {
ConnectionHandler connectionHandler = new ConnectionHandler(this, ssl, connectionHandlers.size() + 1);
connectionHandlers.add(connectionHandler);
} catch (Exception exception) {
ssl.close();
continue;
}
}
} catch (IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public boolean sendPacket(int clientID, Packet packet) throws IOException, ClassNotFoundException {
return getConnectionHandlerByID(clientID).sendPacket(packet);
}
public boolean sendPacket(Packet packet, int clientID) throws IOException, ClassNotFoundException {
return sendPacket(clientID, packet);
} catch (Exception e) { e.printStackTrace(); }
}
// --- Builder ---
public static class ServerBuilder extends DefaultMethodsOverrider {
private int port;
private PacketHandler packetHandler;
private EventManager eventManager;
private Logger logger;
private int timeout = 5000;
private SSLServerSocketFactory factory;
private boolean requireClientCert;
private File caFolder;
private File serverCertFile;
private File serverKeyFile;
private int maxRestartAttempts = 0;
private int restartDelay = 3000;
private int timeout = 0;
public ServerBuilder setPort(int port) { this.port = port; return this; }
public ServerBuilder setPacketHandler(PacketHandler handler) { this.packetHandler = handler; return this; }
public ServerBuilder setEventManager(EventManager manager) { this.eventManager = manager; return this; }
public ServerBuilder setLogger(Logger logger) { this.logger = logger; return this; }
public ServerBuilder setTimeout(int timeout) { this.timeout = timeout; return this; }
public ServerBuilder setSSLServerSocketFactory(SSLServerSocketFactory factory) { this.factory = factory; return this; }
public ServerBuilder setRequireClientCertificate(boolean requireClientCertificate) { this.requireClientCert = requireClientCertificate; return this; }
public ServerBuilder setRootCAFolder(File folder) { this.caFolder = folder; return this; }
public ServerBuilder setServerCertificate(File certFile, File keyFile) { this.serverCertFile = certFile; this.serverKeyFile = keyFile; return this; }
public final NetworkServer build() {
return new NetworkServer(port, packetHandler, eventManager, logger, maxRestartAttempts, restartDelay, timeout);
public NetworkServer build() {
if (factory == null && caFolder != null && serverCertFile != null && serverKeyFile != null) {
try { factory = createSSLServerSocketFactory(caFolder, serverCertFile, serverKeyFile); }
catch (Exception e) { throw new RuntimeException("Failed to create SSLServerSocketFactory", e); }
}
return new NetworkServer(port, packetHandler, eventManager, logger, timeout, factory, requireClientCert);
}
public final ServerBuilder setEventManager(EventManager eventManager) {
this.eventManager = eventManager;
return this;
}
public static SSLServerSocketFactory createSSLServerSocketFactory(File caFolder, File serverCert, File serverKey) throws Exception {
// TrustStore (Root-CAs)
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
public final ServerBuilder setLogger(Logger logger) {
this.logger = logger;
return this;
}
int caIndex = 1;
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
try (FileInputStream fis = new FileInputStream(caFile)) {
java.security.cert.Certificate cert = PemUtils.loadCertificate(caFile);
trustStore.setCertificateEntry("ca" + (caIndex++), cert);
}
}
public final ServerBuilder setMaxReconnectAttempts(int maxRestartAttempts) {
this.maxRestartAttempts = maxRestartAttempts;
return this;
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
public final ServerBuilder setPacketHandler(PacketHandler packetHandler) {
this.packetHandler = packetHandler;
return this;
}
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
java.security.PrivateKey key = PemUtils.loadPrivateKey(serverKey);
java.security.cert.Certificate cert = PemUtils.loadCertificate(serverCert);
keyStore.setKeyEntry("server", key, null, new java.security.cert.Certificate[]{cert});
public final ServerBuilder setPort(int port) {
this.port = port;
return this;
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, null);
public final ServerBuilder setReconnectDelay(int reconnectDelay) {
this.restartDelay = reconnectDelay;
return this;
}
public final ServerBuilder setTimeout(int timeout) {
this.timeout = timeout;
return this;
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext.getServerSocketFactory();
}
}
}

View File

@@ -11,13 +11,16 @@ package me.finn.unlegitlibrary.network.system.server.events;
import me.finn.unlegitlibrary.event.impl.CancellableEvent;
import me.finn.unlegitlibrary.network.system.server.NetworkServer;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.net.Socket;
import java.security.cert.X509Certificate;
public class IncomingConnectionEvent extends CancellableEvent {
public final NetworkServer server;
public final Socket socket;
public final SSLSocket socket;
public IncomingConnectionEvent(NetworkServer server, Socket socket) {
public IncomingConnectionEvent(NetworkServer server, SSLSocket socket) {
this.server = server;
this.socket = socket;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
* See LICENSE-File if exists
*/
package me.finn.unlegitlibrary.network.utils;
import java.io.*;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
public class PemUtils {
public static PrivateKey loadPrivateKey(File keyFile) throws Exception {
String keyPem = new String(Files.readAllBytes(keyFile.toPath()))
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(keyPem);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA"); // oder "EC" je nach Key
return kf.generatePrivate(spec);
}
public static X509Certificate loadCertificate(File certFile) throws Exception {
try (FileInputStream fis = new FileInputStream(certFile)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(fis);
}
}
}