PandaLib/Net/src/main/java/fr/pandacube/lib/net/PServer.java

158 lines
4.1 KiB
Java

package fr.pandacube.lib.net;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.annotations.Beta;
import fr.pandacube.lib.util.Log;
@Beta
public class PServer extends Thread implements Closeable {
private static final AtomicInteger connectionCounterId = new AtomicInteger(0);
private final int port;
private ServerSocket socket;
private final String socketName;
private final List<TCPServerClientConnection> clients = Collections.synchronizedList(new ArrayList<>());
private final AtomicBoolean isClosed = new AtomicBoolean(false);
private final List<PPacketListener<PPacket>> globalPacketListeners = Collections.synchronizedList(new ArrayList<>());
private final List<PSocketConnectionListener> clientConnectionListeners = Collections.synchronizedList(new ArrayList<>());
private final String password;
public PServer(int port, String sckName, String password) {
super("PServer " + sckName);
setDaemon(true);
if (port <= 0 || port > 65535) throw new IllegalArgumentException("le numéro de port est invalide");
socketName = sckName;
this.port = port;
this.password = password;
}
@Override
public void run() {
try {
socket = new ServerSocket();
socket.setReceiveBufferSize(PSocket.NETWORK_TCP_BUFFER_SIZE);
socket.setPerformancePreferences(0, 1, 0);
socket.bind(new InetSocketAddress(port));
while (true) {
Socket socketClient = socket.accept();
socketClient.setSendBufferSize(PSocket.NETWORK_TCP_BUFFER_SIZE);
socketClient.setSoTimeout(PSocket.NETWORK_TIMEOUT);
TCPServerClientConnection co = new TCPServerClientConnection(socketClient,
connectionCounterId.getAndIncrement());
co.start();
}
} catch (SocketException ignored) {
} catch (Exception e) {
Log.warning("Plus aucune connexion ne peux être acceptée", e);
}
}
public void addPacketListener(PPacketListener<PPacket> l) {
globalPacketListeners.add(l);
}
public boolean removePacketListener(PPacketListener<PPacket> l) {
return globalPacketListeners.remove(l);
}
public void addConnectionListener(PSocketConnectionListener l) {
clientConnectionListeners.add(l);
}
public void removeConnectionListener(PSocketConnectionListener l) {
clientConnectionListeners.remove(l);
}
protected class TCPServerClientConnection extends PSocket {
boolean loggedIn;
private TCPServerClientConnection(Socket s, int coId) {
super(s, "Conn#" + coId + " via PServer " + socketName, password);
addConnectionListener(new PSocketConnectionListener() {
@Override
public void onDisconnect(PSocket connection) {
try {
clientConnectionListeners.forEach(l -> l.onDisconnect(connection));
} finally {
clients.remove((TCPServerClientConnection)connection);
}
}
@Override
public void onConnect(PSocket connection) {
clients.add((TCPServerClientConnection)connection);
clientConnectionListeners.forEach(l -> l.onConnect(connection));
}
});
addPacketListener((conn, packet) ->
globalPacketListeners.forEach(l -> {
try {
l.onPacketReceive(conn, packet);
} catch (Exception e) {
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
sendSilently(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
}
})
);
}
}
@Override
public void close() {
try {
if (isClosed.get()) return;
isClosed.set(true);
clients.forEach(PSocket::close);
socket.close();
} catch (IOException ignored) {}
}
public boolean isClosed() {
return isClosed.get() || socket.isClosed();
}
public List<PSocket> getClients() {
synchronized (clients) {
return new ArrayList<>(clients);
}
}
@Override
public String toString() {
return this.getClass().getName() + "{thread=" + getName() + ", socket=" + socket + "}";
}
}