Java SslSocket Bad производительность (как оптимизировать)JAVA

Программисты JAVA общаются здесь
Anonymous
Java SslSocket Bad производительность (как оптимизировать)

Сообщение Anonymous »

Я пытался создать пользовательскую сетевую библиотеку для игрового двигателя в Java, используя Sslsockets с TLS Объяснение проблемы :
В любом случае, так как я прошел некоторые тесты, я заметил, что производительность была очень плоха Всего 5000/с (50/с на клиента), и это продолжается в течение 30S
структура :
Модель сети использует виртуальные потоки Java и прослушивание событий
Он
Тяжелые слушатели также получают свой собственный виртуальный поток, чтобы избежать блокировки
производительность
1. Тест :

Код: Выделить всё

--- Test Results ---
Duration: 35,01 seconds
Clients: 100, Msg Rate/Client: 50/s
Target Total Send Rate: 5000/s
Actual Total Messages Sent (Client): 105272 (3007,03/s)
Total Messages Received (Server): 105272 (3007,03/s)
Client PONGs Received: 84182
Client HEAVY_RESP Received: 21090

--- Latency (Round Trip Nanos) ---
Samples: 84182
Min Latency: 73 801 ns (0,074 ms)
Max Latency: 122 189 501 ns (122,190 ms)
Avg Latency: 1 460 886 ns (1,461 ms)
--------------------
< /code>
[b] 2. Тест [/b]: 
--- Test Results ---
Duration: 35,01 seconds
Clients: 100, Msg Rate/Client: 50/s
Target Total Send Rate: 5000/s
Actual Total Messages Sent (Client): 109337 (3123,38/s)
Total Messages Received (Server): 109337 (3123,38/s)
Client PONGs Received: 87428
Client HEAVY_RESP Received: 21896

--- Latency (Round Trip Nanos) ---
Samples: 87428
Min Latency: 73 999 ns (0,074 ms)
Max Latency: 109 425 701 ns (109,426 ms)
Avg Latency: 1 857 526 ns (1,858 ms)
--------------------
< /code>
[b] 3.  Тест [/b]: 
--- Test Results ---
Duration: 35,00 seconds
Clients: 100, Msg Rate/Client: 50/s
Target Total Send Rate: 5000/s
Actual Total Messages Sent (Client): 103965 (2970,20/s)
Total Messages Received (Server): 103965 (2970,20/s)
Client PONGs Received: 83126
Client HEAVY_RESP Received: 20809

--- Latency (Round Trip Nanos) ---
Samples: 83126
Min Latency: 75 200 ns (0,075 ms)
Max Latency: 62 947 500 ns (62,948 ms)
Avg Latency: 1 635 833 ns (1,636 ms)
--------------------
code
shrong> lwjg.net.dispatcher

Код: Выделить всё

package LWJG.net;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static LWJG.net.util.bin.ByteWizard.toBytes;
import static LWJG.net.util.bin.ByteWizard.toInteger;

import javax.net.ssl.SSLSocket;

/**
* Dispatcher Object to abstract sending and recieving
* {@link #listeners} Includes a basic listener Map that lets utilities easiely interface with Client requests to the Server
*
WARNING: Only 256 Possible Opcodes for now![/b][b] * @author Lithax
* @version 2025-05-03T1:28
*/
public class Dispatcher implements Runnable {
private final SSLSocket socket;
private final byte[] buffer;
private volatile boolean running;

private final Map listeners = new ConcurrentHashMap();

private static final ExecutorService backgroundTaskExecutor = Executors.newVirtualThreadPerTaskExecutor();

public Dispatcher(SSLSocket socket, int bufsize) {
this.running = true;
this.buffer = new byte[bufsize];
this.socket = socket;
}

/**
* Add new Event Listener
* @param opcode Opcode to be invoked by
* @param l Listener
*/
public void addEventListener(byte opcode, IOEventListener l) {
listeners
.computeIfAbsent(opcode, k -> new CopyOnWriteArrayList())
.add(l);
}

/**
* Remove a existing Event Listener
* @param opcode Opcode to be invoked by
* @param l Listener
*/
public void removeEventListener(byte opcode, IOEventListener l) {
List lst = listeners.get(opcode);
if (lst != null) lst.remove(l);
}

@Override
public void run() {
int payloadLen;
byte code;
int chunkLen;
int readSoFar;
int toRead;
int r;
try (InputStream in = socket.getInputStream()) {
int len;
while ((len = in.read(buffer)) != -1 && running) {
if (len < 5) continue;
code = buffer[0];
payloadLen = toInteger(new byte[]{buffer[1], buffer[2], buffer[3], buffer[4]});

ByteArrayOutputStream stream = new ByteArrayOutputStream(payloadLen);

chunkLen = len - 5;
if (chunkLen > 0) {
stream.write(buffer, 5, chunkLen);
}

readSoFar = chunkLen;
while (readSoFar < payloadLen) {
toRead = Math.min(buffer.length, payloadLen - readSoFar);
r = in.read(buffer, 0, toRead);
if (r == -1) throw new IOException("Stream closed prematurely");
stream.write(buffer, 0, r); // only writes actual read instead of entire buffer
readSoFar += r;
}

final byte[] payload = stream.toByteArray();

List lst = listeners.get(code);

if (lst != null)
for (IOEventListener l : lst)
if(l.isHeavyTask())
backgroundTaskExecutor.submit(() -> {
try { l.onEvent(this, payload);  } catch (Exception e) { /* handle */ }
});
else l.onEvent(this, payload);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close();
}
}

/**
* Send a payload to Client
* @param b payload
* @param opcode opcode
*/
public void send(byte opcode, byte[] b) {
byte[] tmp = new byte[b.length+5];
tmp[0] = opcode;
byte[] len = toBytes(b.length);
tmp[1] = len[0];
tmp[2] = len[1];
tmp[3] = len[2];
tmp[4] = len[3];
for(int i = 0; i < b.length; i++) tmp[5+i] = b[i];
send(tmp);
}

/**
* Send raw bytes to Client
* WARNING: Does not account for opcode or len
* @param b Bytes to be send
*/
public void send(byte[] b) {
try {
socket.getOutputStream().write(b);
socket.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* Get Client Socket
* @return Socket
*/
public Socket getSocket() {
return socket;
}

/**
* Close Client[/b][b]     * Warning: After close, Client Object should be removed, as it is now unusable
*/
public void close() {
if(!running) return;
try {
running = false;
if(socket.isClosed()) socket.close();
} catch (IOException e) {
System.out.println("Error Closing Client: "+e.getMessage());
}
}
}
lwjg.net.client

Код: Выделить всё

package LWJG.net;

import javax.net.ssl.SSLSocket;

/**
* Server Client Object, abstracts client sockets for easier handling
* {@link #listeners} Includes a basic listener Map that lets utilities easiely interface with Client requests to the Server
*
WARNING: Only 256 Possible Opcodes for now![/b][b] * @author Lithax
* @version 2025-05-03T1:28
* @side Server
*/
public class Client extends Dispatcher {
private ClientPermission permission;

public Client(SSLSocket socket, ClientPermission perm, int bufsize) {
super(socket, bufsize);
this.permission = perm;
}

/**
* Get Permissions
* @return Permissions
*/
public ClientPermission getPermission() {
return permission;
}

/**
* Set Permissions
* @param permission New Permission
*/
public void setPermission(ClientPermission permission) {
this.permission = permission;
}
}
lwjg.net.clientmanager

Код: Выделить всё

package LWJG.net;

import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.SSLSocket;

/**
* Client Manager Class to manage all current Clients connected to the Server
* {@link #clients} All currently connected clients
* {@link #blacklist} All currently blacklisted clients (aborted upon connection attempt)
* {@link #defaultPerm} The default permission all clients should recieve
* {@link #clientConnectedListeners} All Event Listeners that will be invoked once a new client has connected
* @author Lithax
* @version 2025-05-03T3:58
* @side Server
*/
public class ClientManager {
private List clients;
private List blacklist;
private ClientPermission defaultPerm;

private final List  clientConnectedListeners = new ArrayList();

public ClientManager(ClientPermission permission) {
this.clients = new ArrayList();
this.blacklist = new ArrayList();
this.defaultPerm = permission;
}

/**
* Adds a new Client Connected Listener
* @param listener Listener
*/
public void addClientConnectedListener(ClientConnectedListener listener) {
clientConnectedListeners.add(listener);
}

/**
* Removes a new Client Connected Listener
* @param listener Listener
*/
public void removeClientConnectedListener(ClientConnectedListener listener) {
clientConnectedListeners.remove(listener);
}

/**
* Get current Clients
* @return List clients
*/
public List getClients() {
return clients;
}

public Client add(SSLSocket c) {
if(true || !clients.stream().anyMatch(client -> client.getSocket().getInetAddress().getHostAddress().equals(c.getInetAddress().getHostAddress()))) {
Client client = new Client(c, defaultPerm, 1024);
clients.add(client);
for (ClientConnectedListener listener : clientConnectedListeners) {
listener.onClientConnected(client);
}
return client;
}
return null;
}

/**
* Broadcast raw bytes to all Clients
* @param opcode Listener Opcode
* @param b Bytes
*/
public void broadcast(byte opcode, byte[] b) {
for (Client c : clients) {
c.send(opcode, b);
}
}

/**
* Get Client by String ip representation
* @param ip IP, e.g. 192.168.178.1
* @return Client
*/
public Client get(String ip) {
for(Client c : clients)
if(c.getSocket().getInetAddress().getHostAddress().equals(ip)) {
return c;
}
return null;
}

/**
* Close Client by IP
* @param ip IP, e.g.  192.168.178.1
* @return Closing Sucessful
*/
public boolean close(String ip) {
Client c = get(ip);
if(c != null) { c.close(); clients.remove(c); return true; } return false;
}

/**
* Checks if a client is blacklisted
* @param ip Client ip
* @return Is blocked
*/
public boolean isBlocked(String ip) {
return blacklist.contains(ip);
}

/**
* Add Client to blacklist
* @param ip
*/
public void block(String ip) {
if(!blacklist.contains(ip)) blacklist.add(ip);
}

/**
* Remove Client from blacklist
* @param ip
*/
public void unblock(String ip) {
if(blacklist.contains(ip)) blacklist.remove(ip);
}

/**
* Close all clients
*/
public void closeAllClients() {
for(Client c : clients) c.close();
}
}
lwjg.net.remoteserver

Код: Выделить всё

package LWJG.net;

import java.io.IOException;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/**
* Client side of the Client Server interaction
* {@link #listeners} Includes a basic listener Map that lets utilities easiely interface with Client requests to the Server
*
WARNING: Only 256 Possible Opcodes for now![/b]
* @author Lithax
* @version 2025-05-03T1:28
* @side Client
*/
public class RemoteServer extends Dispatcher {
private final int port;
private final String ip;

public RemoteServer(String ip, int port, int bufsize) throws IOException {
super(createSocket(ip, port), bufsize);
this.port = port;
this.ip = ip;
}

private static SSLSocket createSocket(String ip, int port) throws IOException {
SSLSocket s = (SSLSocket) SSLSocketFactory.getDefault().createSocket(ip, port);
s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" });
return s;
}

public String getIP() {
return ip;
}

public int getPort() {
return port;
}
}
lwjg.net.serverhandler

Код: Выделить всё

package LWJG.net;

import java.io.IOException;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

/**
* Utility Class for easy creation of a server with Clientmanager integretation
* @author Lithax
* @version 2025-05-03T4:03
* @side Server
*/
public class ServerHandler extends Thread {
private final int port;
private final int backlog;
private volatile boolean running;
private ClientManager mn;
private SSLServerSocket serverSocket;

static {
System.setProperty("javax.net.ssl.keyStore", "server.keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "server.password");
}

public ServerHandler(int port, int backlog, ClientPermission defaultPermission) {
this.port = port;
this.backlog = backlog;
this.running = true;
mn = new ClientManager(defaultPermission);
}

@Override
public void run() {
try {
serverSocket = (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket(port, backlog);
serverSocket.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" });
while(running) {
SSLSocket client = (SSLSocket) serverSocket.accept();
if(!mn.isBlocked(client.getInetAddress().getHostAddress())) {
Client newClient = mn.add(client);
if (newClient != null) {
Thread.startVirtualThread(newClient);
System.out.println("Started virtual thread for client");
} else {
System.out.println("Failed to add client (duplicate or error");
if (!client.isClosed()) try { client.close();  } catch (IOException e) { /* ignore */ }
}
} else System.out.println("Client connection was blocked.");
}
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
finally {
close();
}
}

public void close() {
running = false;
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
} catch (IOException e) {
System.err.println("Error closing server socket: " + e.getMessage());
}
}
mn.closeAllClients();
}

public ClientManager getClientManager() {
return mn;
}
}
lwjg.net.ioeventlistener> /> Если вы знаете способ сделать это быстрее, мне было бы очень интересно, я пытаюсь получить максимальную производительность < /p>

Подробнее здесь: https://stackoverflow.com/questions/796 ... o-optimize

Вернуться в «JAVA»