Full refactor of network API + deprecation of old API
This commit is contained in:
parent
c510c0197e
commit
0c3e1425fd
@ -1,8 +1,8 @@
|
||||
package fr.pandacube.util.network.packet.bytebuffer;
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Array8Bit implements ByteSerializable {
|
||||
public class Array8Bit {
|
||||
|
||||
public static final int BIT_COUNT = Byte.SIZE;
|
||||
|
||||
@ -63,15 +63,4 @@ public class Array8Bit implements ByteSerializable {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
fromByte(buffer.getByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putByte(toByte());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,27 +1,32 @@
|
||||
package fr.pandacube.util.network.packet.bytebuffer;
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ByteBuffer implements Cloneable {
|
||||
import fr.pandacube.Pandacube;
|
||||
|
||||
public final class ByteBuffer implements Cloneable {
|
||||
|
||||
private java.nio.ByteBuffer buff;
|
||||
private Charset charset;
|
||||
|
||||
public ByteBuffer(Charset c) {
|
||||
this(16, c);
|
||||
public ByteBuffer() {
|
||||
this(16);
|
||||
}
|
||||
|
||||
public ByteBuffer(int initSize, Charset c) {
|
||||
public ByteBuffer(int initSize) {
|
||||
buff = java.nio.ByteBuffer.allocate(initSize);
|
||||
charset = c;
|
||||
}
|
||||
|
||||
public ByteBuffer(byte[] data, Charset c) {
|
||||
buff = java.nio.ByteBuffer.wrap(Arrays.copyOf(data, data.length));
|
||||
charset = c;
|
||||
/**
|
||||
* Create a ByteBuffer that is initially <b>backed</b> by the provided byte array.
|
||||
* The position of this buffer will be 0.
|
||||
* If this ByteBuffer needs a biffer array, the provided array is replaced by a new one,
|
||||
* making the provided array not related to this ByteBuffer anymore.
|
||||
* @param data array of byte that serve as a backend for this ByteBuffer.
|
||||
*/
|
||||
public ByteBuffer(byte[] data) {
|
||||
buff = java.nio.ByteBuffer.wrap(data);
|
||||
}
|
||||
|
||||
private void askForBufferExtension(int needed) {
|
||||
@ -32,9 +37,12 @@ public class ByteBuffer implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This clone method also clone the underlying array.
|
||||
*/
|
||||
@Override
|
||||
public ByteBuffer clone() {
|
||||
return new ByteBuffer(Arrays.copyOf(buff.array(), buff.array().length), charset);
|
||||
return new ByteBuffer(Arrays.copyOf(buff.array(), buff.array().length));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,7 +223,7 @@ public class ByteBuffer implements Cloneable {
|
||||
if (s == null) {
|
||||
return putInt(-1);
|
||||
}
|
||||
return putSizedByteArray(s.getBytes(charset));
|
||||
return putSizedByteArray(s.getBytes(Pandacube.NETWORK_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,51 +232,7 @@ public class ByteBuffer implements Cloneable {
|
||||
*/
|
||||
public String getString() {
|
||||
byte[] binaryString = getSizedByteArray();
|
||||
return (binaryString == null) ? null : new String(binaryString, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The objet will be serialized and the data put in the current buffer
|
||||
*
|
||||
* @param obj the object to serialize. Can't be null.
|
||||
* @return the current buffer
|
||||
*/
|
||||
public ByteBuffer putObject(ByteSerializable obj) {
|
||||
obj.serializeToByteBuffer(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask to object passed as argument to deserialize data in buffer and fill
|
||||
* the object content. ByteSerializable object are never null.
|
||||
*
|
||||
* @param <T>
|
||||
* @param clazz the class wich will be instanciated with his no-argument Constructor
|
||||
* before filled by using {@link ByteSerializable#deserializeFromByteBuffer(ByteBuffer)}
|
||||
* @return obj a reference to the filled object
|
||||
*/
|
||||
public <T extends ByteSerializable> T getObject(Class<T> clazz) {
|
||||
try {
|
||||
T obj = clazz.newInstance();
|
||||
obj.deserializeFromByteBuffer(this);
|
||||
return obj;
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException("A ByteSerializable must have a no-argument Constructor", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param list The list itself can be null, but not the values.
|
||||
* @return
|
||||
*/
|
||||
public ByteBuffer putListObject(List<ByteSerializable> list) {
|
||||
if (list.stream().anyMatch(e -> e == null))
|
||||
throw new IllegalArgumentException("List of object can't contains any null value");
|
||||
putInt(list.size());
|
||||
for (ByteSerializable obj : list)
|
||||
putObject(obj);
|
||||
return this;
|
||||
return (binaryString == null) ? null : new String(binaryString, Pandacube.NETWORK_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,25 +250,10 @@ public class ByteBuffer implements Cloneable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clazz
|
||||
* @return Can be null. If not, there is no null element inside.
|
||||
*/
|
||||
public <T extends ByteSerializable> List<T> getListObject(Class<T> clazz) {
|
||||
int size = getInt();
|
||||
if (size < 0)
|
||||
return null;
|
||||
List<T> list = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++)
|
||||
list.add(getObject(clazz));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of String. The list can be null, and any element can be null too.
|
||||
*/
|
||||
public <T extends ByteSerializable> List<String> getListOfString() {
|
||||
public List<String> getListOfString() {
|
||||
int size = getInt();
|
||||
if (size < 0)
|
||||
return null;
|
57
src/main/java/fr/pandacube/util/net/PPacket.java
Normal file
57
src/main/java/fr/pandacube/util/net/PPacket.java
Normal file
@ -0,0 +1,57 @@
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PPacket {
|
||||
public String name;
|
||||
/* package */ int id;
|
||||
public byte[] content;
|
||||
|
||||
/**
|
||||
* Construct a new PPacket based on the content of the provided buffer before his position.
|
||||
* @param n the name of the packet.
|
||||
* @param buff the buffer where the data comes from. Only the content before {@link ByteBuffer#getPosition()} is copied.
|
||||
*/
|
||||
public PPacket(String n, ByteBuffer buff) {
|
||||
this(n, Arrays.copyOf(buff.array(), buff.getPosition()));
|
||||
}
|
||||
|
||||
public PPacket(String n, byte[] c) {
|
||||
name = n;
|
||||
content = c;
|
||||
}
|
||||
|
||||
/* package */ PPacket(String n, int i, byte[] c) {
|
||||
this(n, c);
|
||||
id = i;
|
||||
}
|
||||
|
||||
public ByteBuffer getContentAsBuffer() {
|
||||
return new ByteBuffer(content);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static PPacket buildSingleStringContentPacket(String name, String content) {
|
||||
return new PPacket(name, new ByteBuffer().putString(content));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ static PPacket buildLoginPacket(String password) {
|
||||
return buildSingleStringContentPacket("login", password);
|
||||
}
|
||||
/* package */ static PPacket buildBadFormatPacket(String message) {
|
||||
return buildSingleStringContentPacket("bad_format", message);
|
||||
}
|
||||
/* package */ static PPacket buildLoginBadPacket() {
|
||||
return new PPacket("login_bad", new byte[0]);
|
||||
}
|
||||
}
|
44
src/main/java/fr/pandacube/util/net/PPacketAnswer.java
Normal file
44
src/main/java/fr/pandacube/util/net/PPacketAnswer.java
Normal file
@ -0,0 +1,44 @@
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PPacketAnswer extends PPacket {
|
||||
/* package */ int answer;
|
||||
|
||||
/**
|
||||
* Construct a new PPacketAnswer based on the content of the provided buffer before his position.
|
||||
* @param n the name of the packet.
|
||||
* @param buff the buffer where the data comes from. Only the content before {@link ByteBuffer#getPosition()} is copied.
|
||||
*/
|
||||
public PPacketAnswer(PPacket answered, String n, ByteBuffer buff) {
|
||||
this(answered, n, Arrays.copyOf(buff.array(), buff.getPosition()));
|
||||
}
|
||||
|
||||
public PPacketAnswer(PPacket answered, String n, byte[] c) {
|
||||
super(n, c);
|
||||
answer = answered.id;
|
||||
}
|
||||
|
||||
/* package */ PPacketAnswer(String n, int i, int a, byte[] c) {
|
||||
super(n, i, c);
|
||||
answer = a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static PPacketAnswer buildSingleStringContentPacketAnswer(PPacket answered, String name, String content) {
|
||||
ByteBuffer pwBuff = new ByteBuffer().putString(content);
|
||||
return new PPacketAnswer(answered, name, Arrays.copyOf(pwBuff.array(), pwBuff.getPosition()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ static PPacketAnswer buildLoginOkPacket(PPacket loginPacket) {
|
||||
return new PPacketAnswer(loginPacket, "login_ok", new byte[0]);
|
||||
}
|
||||
/* package */ static PPacketAnswer buildExceptionPacket(PPacket answered, String message) {
|
||||
return buildSingleStringContentPacketAnswer(answered, "exception", message);
|
||||
}
|
||||
}
|
13
src/main/java/fr/pandacube/util/net/PPacketListener.java
Normal file
13
src/main/java/fr/pandacube/util/net/PPacketListener.java
Normal file
@ -0,0 +1,13 @@
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PPacketListener<P extends PPacket> {
|
||||
|
||||
/**
|
||||
* Called when we receive a packet (except responses)
|
||||
* @param connection the connection from where the packet comes
|
||||
* @param packet the received packet
|
||||
*/
|
||||
public void onPacketReceive(PSocket connection, P packet);
|
||||
|
||||
}
|
165
src/main/java/fr/pandacube/util/net/PServer.java
Normal file
165
src/main/java/fr/pandacube/util/net/PServer.java
Normal file
@ -0,0 +1,165 @@
|
||||
package fr.pandacube.util.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 org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import fr.pandacube.Pandacube;
|
||||
import fr.pandacube.util.Log;
|
||||
|
||||
public class PServer extends Thread implements Closeable {
|
||||
private static AtomicInteger connectionCounterId = new AtomicInteger(0);
|
||||
|
||||
private int port;
|
||||
private ServerSocket socket;
|
||||
private String socketName;
|
||||
|
||||
private List<TCPServerClientConnection> clients = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
|
||||
private List<PPacketListener<PPacket>> globalPacketListeners = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<PSocketConnectionListener> clientConnectionListeners = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
|
||||
|
||||
private String password;
|
||||
|
||||
public PServer(int port, String sckName, String password) throws IOException {
|
||||
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(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setPerformancePreferences(0, 1, 0);
|
||||
socket.bind(new InetSocketAddress(port));
|
||||
|
||||
while (true) {
|
||||
Socket socketClient = socket.accept();
|
||||
socketClient.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socketClient.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
|
||||
try {
|
||||
@SuppressWarnings("resource")
|
||||
TCPServerClientConnection co = new TCPServerClientConnection(socketClient,
|
||||
connectionCounterId.getAndIncrement());
|
||||
co.start();
|
||||
} catch (IOException e) {
|
||||
Log.severe("Connexion impossible avec " + socketClient.getInetAddress());
|
||||
}
|
||||
}
|
||||
} catch(SocketException e) {
|
||||
|
||||
} 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) throws IOException {
|
||||
super(s, "Conn#" + coId + " via TCPSv " + 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 e) {}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<PSocket> getClients() {
|
||||
synchronized (clients) {
|
||||
return new ArrayList<>(clients);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket).toString();
|
||||
}
|
||||
|
||||
}
|
351
src/main/java/fr/pandacube/util/net/PSocket.java
Normal file
351
src/main/java/fr/pandacube/util/net/PSocket.java
Normal file
@ -0,0 +1,351 @@
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import fr.pandacube.Pandacube;
|
||||
import fr.pandacube.util.Log;
|
||||
|
||||
/**
|
||||
* A wrapper for a {@link Socket}. The connection must point to a software using {@link PServer}
|
||||
* as wrapper for the target {@link ServerSocket}.
|
||||
* <br>
|
||||
* This class provides a simple way to exchange data between client and server :
|
||||
* <li>Maintained connection with the server</li>
|
||||
* <li>Login with a password (send in the first packet)</li>
|
||||
* <li>Named packets</li>
|
||||
* <li>Binary data</li>
|
||||
* <li>Input stream in a separate Thread</li>
|
||||
*
|
||||
*/
|
||||
public class PSocket extends Thread implements Closeable {
|
||||
|
||||
private boolean server = false;
|
||||
private Socket socket;
|
||||
private SocketAddress addr;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
private Object outSynchronizer = new Object();
|
||||
private String password;
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
private List<PPacketListener<PPacket>> packetListeners = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<PSocketConnectionListener> connectionListeners = Collections.synchronizedList(new ArrayList<>());
|
||||
private Map<Integer, PPacketListener<PPacketAnswer>> answersCallbacks = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
private int nextSendId = 0;
|
||||
|
||||
/**
|
||||
* Create a new PSocket that will connect to the specified SocketAddress.
|
||||
* @param a The target server to connect to
|
||||
* @param connName the name of the connection, used to name the Thread used to receive the packet.
|
||||
* @param the password to send to the server.
|
||||
*/
|
||||
public PSocket(SocketAddress a, String connName, String pass) {
|
||||
super("PSocket " + connName);
|
||||
setDaemon(true);
|
||||
if (a == null) throw new IllegalArgumentException("les arguments ne peuvent pas être null");
|
||||
addr = a;
|
||||
}
|
||||
|
||||
|
||||
/* package */ PSocket(Socket s, String connName, String pass) {
|
||||
this(s.getRemoteSocketAddress(), connName, pass);
|
||||
socket = s;
|
||||
server = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
if (socket == null) {
|
||||
socket = new Socket();
|
||||
socket.setReceiveBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
|
||||
socket.setSoTimeout(10000); // initial timeout before login
|
||||
|
||||
socket.connect(addr);
|
||||
|
||||
in = new DataInputStream(socket.getInputStream());
|
||||
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
|
||||
}
|
||||
|
||||
// password check
|
||||
if (server) {
|
||||
PPacket packet = readPacket();
|
||||
if (packet == null || packet instanceof PPacketAnswer || !"login".equals(packet.name)) {
|
||||
send(PPacket.buildLoginBadPacket());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String receivedPassword = new ByteBuffer(packet.content).getString();
|
||||
if (!Objects.equal(receivedPassword, password)) {
|
||||
send(PPacket.buildLoginBadPacket());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
send(PPacket.buildLoginBadPacket());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
send(PPacketAnswer.buildLoginOkPacket(packet));
|
||||
// login ok at this point
|
||||
password = null;
|
||||
}
|
||||
else {
|
||||
send(PPacket.buildLoginPacket(password));
|
||||
PPacket packet = readPacket();
|
||||
if (packet == null) {
|
||||
Log.severe("bad packet received from server. Disconnecting.");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (packet.name.equals("login_bad")) {
|
||||
Log.severe("Wrong password to connect to server. Disconnecting.");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (!packet.name.equals("login_ok")) {
|
||||
Log.severe("Unexpected packet from server. Disconnecting.");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
// login ok at this point
|
||||
password = null;
|
||||
}
|
||||
|
||||
socket.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
|
||||
Log.info(getName() + " connected.");
|
||||
|
||||
connectionListeners.forEach(l -> {
|
||||
try {
|
||||
l.onConnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling PSocketConnectionListener.onConnect().", e);
|
||||
}
|
||||
});
|
||||
|
||||
while (!socket.isClosed()) {
|
||||
PPacket packet = readPacket();
|
||||
|
||||
if (packet == null) {
|
||||
send(PPacket.buildBadFormatPacket("Bad format for the last packet received. Closing connection."));
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet instanceof PPacketAnswer) {
|
||||
try {
|
||||
answersCallbacks.remove(((PPacketAnswer)packet).answer).onPacketReceive(this, (PPacketAnswer)packet);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
|
||||
send(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
packetListeners.forEach(l -> {
|
||||
try {
|
||||
l.onPacketReceive(this, packet);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
|
||||
sendSilently(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the packet read in the socket, or null if the packet is in a bad format.
|
||||
* @return the packet
|
||||
* @throws IOException
|
||||
*
|
||||
*/
|
||||
private PPacket readPacket() throws IOException {
|
||||
byte nSize = in.readByte();
|
||||
if (nSize == 0) {
|
||||
return null;
|
||||
}
|
||||
boolean answer = nSize < 0;
|
||||
if (answer)
|
||||
nSize *= -1;
|
||||
|
||||
|
||||
byte[] nBytes = new byte[nSize];
|
||||
in.readFully(nBytes);
|
||||
String name = new String(nBytes, Pandacube.NETWORK_CHARSET);
|
||||
|
||||
|
||||
int packetId = in.readInt();
|
||||
|
||||
|
||||
int answerId = (answer) ? in.readInt() : -1;
|
||||
|
||||
|
||||
int cSize = in.readInt();
|
||||
if (cSize < 0 || cSize > 0xFFFFFF) { // can't be more that 16 MiB
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
byte[] content = new byte[cSize];
|
||||
in.readFully(content);
|
||||
|
||||
return answer ? new PPacketAnswer(name, packetId, answerId, content) : new PPacket(name, packetId, content);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send the provided packet, without waiting for an answer.
|
||||
* @param packet
|
||||
* @throws IOException
|
||||
*/
|
||||
public void send(PPacket packet) throws IOException {
|
||||
if (packet == null)
|
||||
throw new IllegalArgumentException("packet can't be null");
|
||||
if (packet.name == null)
|
||||
throw new IllegalArgumentException("packet.name can't be null");
|
||||
if (packet.content == null)
|
||||
throw new IllegalArgumentException("packet.content can't be null");
|
||||
|
||||
byte[] nameBytes = packet.name.getBytes(Pandacube.NETWORK_CHARSET);
|
||||
if (nameBytes.length > 127)
|
||||
throw new IllegalArgumentException("packet.name must take fewer than 128 bytes when converted to UTF-8");
|
||||
byte nameSize = (byte)nameBytes.length;
|
||||
|
||||
boolean answer = packet instanceof PPacketAnswer;
|
||||
|
||||
if (answer) nameSize *= -1;
|
||||
|
||||
synchronized (outSynchronizer) {
|
||||
int packetId = nextSendId++;
|
||||
|
||||
packet.id = packetId;
|
||||
|
||||
out.write(new byte[] {nameSize});
|
||||
out.write(nameBytes);
|
||||
out.write(packetId);
|
||||
if (answer)
|
||||
out.write(((PPacketAnswer)packet).answer);
|
||||
out.write(packet.content.length);
|
||||
out.write(packet.content);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSilently(PPacket packet) {
|
||||
try {
|
||||
send(packet);
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void send(PPacket packet, PPacketListener<PPacketAnswer> answerCallback) throws IOException {
|
||||
synchronized (answersCallbacks) {
|
||||
/*
|
||||
* This synch block ensure that the callback will be put in the listeners Map before
|
||||
* we receve the answer (in case this is really really fast)
|
||||
*/
|
||||
send(packet);
|
||||
answersCallbacks.put(packet.id, answerCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void addPacketListener(PPacketListener<PPacket> l) {
|
||||
packetListeners.add(l);
|
||||
}
|
||||
|
||||
public boolean removePacketListener(PPacketListener<PPacket> l) {
|
||||
return packetListeners.remove(l);
|
||||
}
|
||||
|
||||
|
||||
public void addConnectionListener(PSocketConnectionListener l) {
|
||||
connectionListeners.add(l);
|
||||
}
|
||||
|
||||
public void removeConnectionListener(PSocketConnectionListener l) {
|
||||
connectionListeners.remove(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
synchronized (outSynchronizer) {
|
||||
if (isClosed.get()) return;
|
||||
|
||||
Log.info(getName() + " closing...");
|
||||
|
||||
connectionListeners.forEach(l -> {
|
||||
try {
|
||||
l.onDisconnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling PSocketConnectionListener.onDisconnect().", e);
|
||||
}
|
||||
});
|
||||
|
||||
socket.close();
|
||||
isClosed.set(true);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket.getRemoteSocketAddress()).toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package fr.pandacube.util.net;
|
||||
|
||||
public interface PSocketConnectionListener {
|
||||
|
||||
/**
|
||||
* Called when a socket is connected
|
||||
* @param connection the connection
|
||||
*/
|
||||
public void onConnect(PSocket connection);
|
||||
|
||||
/**
|
||||
* Called just before a socket is disconnected
|
||||
* @param connection the connection
|
||||
*/
|
||||
public void onDisconnect(PSocket connection);
|
||||
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
package fr.pandacube.util.network.client;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.javatuples.Pair;
|
||||
|
||||
import fr.pandacube.Pandacube;
|
||||
import fr.pandacube.util.Log;
|
||||
import fr.pandacube.util.network.packet.Packet;
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.PacketException;
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.ResponseCallback;
|
||||
import fr.pandacube.util.network.packet.packets.global.PacketD0ServerException;
|
||||
|
||||
public class TCPClient extends Thread implements Closeable {
|
||||
|
||||
private Socket socket;
|
||||
private SocketAddress addr;
|
||||
private TCPClientListener listener;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
private Object outSynchronizer = new Object();
|
||||
private List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacks = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacksAvoidListener = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
public TCPClient(InetSocketAddress a, String connName, TCPClientListener l) throws IOException {
|
||||
super("TCPCl " + connName);
|
||||
setDaemon(true);
|
||||
if (a == null || l == null) throw new IllegalArgumentException("les arguments ne peuvent pas être null");
|
||||
socket = new Socket();
|
||||
socket.setReceiveBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
socket.connect(a);
|
||||
addr = a;
|
||||
listener = l;
|
||||
try {
|
||||
listener.onConnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onConnect()", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
byte[] code = new byte[1];
|
||||
while (!socket.isClosed() && in.read(code) != -1) {
|
||||
byte[] sizeB = new byte[4];
|
||||
if (in.read(sizeB) != 4) throw new IOException("Socket " + addr + " fermé");
|
||||
|
||||
int size = ByteBuffer.wrap(sizeB).getInt();
|
||||
|
||||
byte[] content = new byte[size];
|
||||
|
||||
forceReadBytes(content);
|
||||
|
||||
byte[] packetData = ByteBuffer.allocate(1 + 4 + size).put(code).put(sizeB).put(content).array();
|
||||
|
||||
try {
|
||||
|
||||
Packet p = Packet.constructPacket(packetData);
|
||||
|
||||
if (!(p instanceof PacketServer))
|
||||
throw new PacketException(p.getClass().getCanonicalName() + " is not a subclass of PacketServer");
|
||||
|
||||
if (p instanceof PacketD0ServerException) {
|
||||
|
||||
try {
|
||||
listener.onServerException(this, ((PacketD0ServerException)p).getExceptionString());
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onServerException()", e);
|
||||
}
|
||||
}
|
||||
|
||||
PacketServer ps = (PacketServer) p;
|
||||
|
||||
boolean callbackExecuted = executeCallbacks(ps, callbacksAvoidListener);
|
||||
|
||||
try {
|
||||
if (!callbackExecuted)
|
||||
listener.onPacketReceive(this, ps);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onPacketReceive()", e);
|
||||
}
|
||||
|
||||
executeCallbacks(ps, callbacks);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while handling packet from server", e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
private boolean executeCallbacks(PacketServer ps, List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacks) {
|
||||
boolean executedOne = false;
|
||||
synchronized (callbacks) {
|
||||
for(Iterator<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> it = callbacks.iterator(); it.hasNext();) {
|
||||
Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>> c = it.next();
|
||||
try {
|
||||
if (c.getValue0().test(ps)) {
|
||||
it.remove();
|
||||
c.getValue1().call(ps);
|
||||
executedOne = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while executing callback", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return executedOne;
|
||||
}
|
||||
|
||||
|
||||
private void forceReadBytes(byte[] buff) throws IOException {
|
||||
int pos = 0;
|
||||
do {
|
||||
int nbR = in.read(buff, pos, buff.length - pos);
|
||||
if (nbR == -1) throw new IOException("Can't read required amount of byte");
|
||||
pos += nbR;
|
||||
} while (pos < buff.length);
|
||||
}
|
||||
|
||||
public void send(PacketClient packet) throws IOException {
|
||||
synchronized (outSynchronizer) {
|
||||
out.write(packet.getFullSerializedPacket());
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendAndGetResponse(PacketClient packet, Predicate<PacketServer> responseCondition, ResponseCallback<PacketServer> callback, boolean avoidListener) throws IOException {
|
||||
Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>> p = new Pair<>(responseCondition, callback);
|
||||
if (avoidListener)
|
||||
callbacksAvoidListener.add(p);
|
||||
else
|
||||
callbacks.add(p);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
|
||||
public PacketServer sendAndWaitForResponse(PacketClient packet, Predicate<PacketServer> responseCondition) throws IOException, InterruptedException {
|
||||
AtomicReference<PacketServer> psStorage = new AtomicReference<>(null);
|
||||
synchronized (psStorage) {
|
||||
sendAndGetResponse(packet, responseCondition, packetServer -> {
|
||||
synchronized (psStorage) {
|
||||
psStorage.set(packetServer);
|
||||
psStorage.notifyAll();
|
||||
}
|
||||
}, true);
|
||||
|
||||
psStorage.wait();
|
||||
return psStorage.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
synchronized (outSynchronizer) {
|
||||
if (isClosed.get()) return;
|
||||
socket.close();
|
||||
isClosed.set(true);
|
||||
|
||||
try {
|
||||
listener.onDisconnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onDisconnect()", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSilently(PacketClient packet) {
|
||||
try {
|
||||
send(packet);
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public SocketAddress getServerAddress() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket.getRemoteSocketAddress()).toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package fr.pandacube.util.network.client;
|
||||
|
||||
import fr.pandacube.util.Log;
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
|
||||
public interface TCPClientListener {
|
||||
|
||||
/**
|
||||
* Called when a connection is opened
|
||||
* @param connection the connection which is opening
|
||||
*/
|
||||
public void onConnect(TCPClient connection);
|
||||
|
||||
/**
|
||||
* Called when the server send us a PacketServerException when an exception is thrown
|
||||
* server side when handling our packets or eventually when the client is concerned
|
||||
* with this error.<br/>
|
||||
* The default implementation of this method just call the internal Logger of PandacubeUtil
|
||||
* to print the stacktrace of the Exception.
|
||||
* @param connection the connection which just received the error
|
||||
* @param exceptionString a string representation of the exception. If the server
|
||||
* use this Java library, it may be a full representation of
|
||||
* {@link Throwable#printStackTrace()}.
|
||||
*/
|
||||
public default void onServerException(TCPClient connection, String exceptionString) {
|
||||
Log.severe("Exception thrown by server through " + connection + " : \n"+exceptionString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server send us a packet
|
||||
* @param connection the connection where the packet come from
|
||||
* @param packet the packet
|
||||
*/
|
||||
public void onPacketReceive(TCPClient connection, PacketServer packet);
|
||||
|
||||
/**
|
||||
* Called before the connection closed
|
||||
* @param connection the connection which is closing
|
||||
*/
|
||||
public void onDisconnect(TCPClient connection);
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package fr.pandacube.util.network.packet;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import fr.pandacube.Pandacube;
|
||||
import fr.pandacube.util.Log;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteSerializable;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.Packet30ClientDeclareProcess;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.Packet31ClientClose;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.Packet32ClientProcessQueryResponse;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB0ServerClose;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB1ServerProcessDeclarationConfirm;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB2ServerConnectSuccess;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB3ServerProcessInput;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB4ServerProcessQuery;
|
||||
import fr.pandacube.util.network.packet.packets.global.Packet50ClientAuthenticate;
|
||||
import fr.pandacube.util.network.packet.packets.global.Packet51ClientLogRecord;
|
||||
import fr.pandacube.util.network.packet.packets.global.PacketD0ServerException;
|
||||
import fr.pandacube.util.network.packet.packets.global.PacketD1ServerCantAuthenticate;
|
||||
import fr.pandacube.util.network.packet.packets.web.Packet00ClientWebRequest;
|
||||
import fr.pandacube.util.network.packet.packets.web.Packet80ServerWebResponse;
|
||||
|
||||
/** <pre>
|
||||
* Identification des packets réseaux
|
||||
* byte (xxxxxxxx)
|
||||
* client : server :
|
||||
* clt / sv (x-------) (0-------) (1-------)
|
||||
* 0x00 - 0x7F 0x80 - 0xFF
|
||||
* use case (-xxx----)
|
||||
* - web (-000----) 0x00 - 0x0F 0x80 - 0x8F (client is Apache, server is PandacubeCore master (PandacubeWeb))
|
||||
* - spigot (-001----) 0x10 - 0x1F 0x90 - 0x9F (client is PandacubeSpigot, server is PandacubeCore master)
|
||||
* - bungee (-010----) 0x20 - 0x2F 0xA0 - 0xAF (client is PandacubeBungee, server is PandacubeCore master)
|
||||
* -coreslave(-011----) 0x30 - 0x3F 0xB0 - 0xBF (client is PandacubeCore slave, sv is PandacubeCore master)
|
||||
* - global (-101----) 0x50 - 0x5F 0xD0 - 0xDF
|
||||
*
|
||||
* - reserved if not enough packet id in certain use case
|
||||
* (-11x----) 0x60 - 0x7F 0xE0 - 0xFF
|
||||
*
|
||||
* packet id (----xxxx)
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class Packet implements ByteSerializable {
|
||||
|
||||
private final byte code;
|
||||
|
||||
public Packet(byte c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public byte[] getFullSerializedPacket() {
|
||||
ByteBuffer internal = new ByteBuffer(CHARSET).putObject(this);
|
||||
byte[] data = Arrays.copyOfRange(internal.array(), 0, internal.getPosition());
|
||||
|
||||
return new ByteBuffer(5 + data.length, CHARSET).putByte(code).putInt(data.length).putByteArray(data).array();
|
||||
}
|
||||
|
||||
public static final Charset CHARSET = Pandacube.NETWORK_CHARSET;
|
||||
|
||||
private static Map<Byte, Class<? extends Packet>> packetTypes = new HashMap<>();
|
||||
|
||||
public static Packet constructPacket(byte[] data) {
|
||||
if (!packetTypes.containsKey(data[0]))
|
||||
throw new PacketException("Packet identifier not recognized: 0x" + String.format("%02X", data[0])
|
||||
+ ". Maybe this packet is not registered with Packet.addPacket()");
|
||||
|
||||
try {
|
||||
Packet p = packetTypes.get(data[0]).newInstance();
|
||||
ByteBuffer dataBuffer = new ByteBuffer(Arrays.copyOfRange(data, 5, data.length), CHARSET);
|
||||
p.deserializeFromByteBuffer(dataBuffer);
|
||||
return p;
|
||||
} catch (Exception e) {
|
||||
throw new PacketException("Error while constructing packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Packet> void addPacket(Class<T> packetClass) {
|
||||
try {
|
||||
Packet p = packetClass.newInstance();
|
||||
packetTypes.put(p.code, packetClass);
|
||||
} catch (Exception e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
|
||||
/*
|
||||
* Ajout des types de packets (client + serveur)
|
||||
*/
|
||||
addPacket(Packet31ClientClose.class);
|
||||
addPacket(Packet30ClientDeclareProcess.class);
|
||||
addPacket(Packet32ClientProcessQueryResponse.class);
|
||||
addPacket(PacketB0ServerClose.class);
|
||||
addPacket(PacketB2ServerConnectSuccess.class);
|
||||
addPacket(PacketB1ServerProcessDeclarationConfirm.class);
|
||||
addPacket(PacketB3ServerProcessInput.class);
|
||||
addPacket(PacketB4ServerProcessQuery.class);
|
||||
|
||||
addPacket(Packet50ClientAuthenticate.class);
|
||||
addPacket(Packet51ClientLogRecord.class);
|
||||
addPacket(PacketD1ServerCantAuthenticate.class);
|
||||
addPacket(PacketD0ServerException.class);
|
||||
|
||||
addPacket(Packet00ClientWebRequest.class);
|
||||
addPacket(Packet80ServerWebResponse.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package fr.pandacube.util.network.packet;
|
||||
|
||||
/**
|
||||
* On attend d'un instance de {@link PacketClient} qu'il soit envoyé depuis
|
||||
* une connexion Client vers une connexion Serveur (d'un point de vue TCP)
|
||||
*/
|
||||
public abstract class PacketClient extends Packet {
|
||||
|
||||
public PacketClient(byte c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package fr.pandacube.util.network.packet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Thrown when there is a problem when constructing, sending or handling a packet.
|
||||
*
|
||||
* Only the server may send a string representation of an exception to the client, not the reverse
|
||||
*
|
||||
*/
|
||||
public class PacketException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PacketException(String m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
public PacketException(String m, Throwable t) {
|
||||
super(m, t);
|
||||
}
|
||||
|
||||
public PacketException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package fr.pandacube.util.network.packet;
|
||||
|
||||
/**
|
||||
* On attend d'un instance de {@link PacketServer} qu'il soit envoyé depuis
|
||||
* une connexion Serveur vers une connexion Client (d'un point de vue TCP)
|
||||
*/
|
||||
public abstract class PacketServer extends Packet {
|
||||
|
||||
public PacketServer(byte c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package fr.pandacube.util.network.packet;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResponseCallback<T extends Packet> {
|
||||
|
||||
public void call(T packet);
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.bytebuffer;
|
||||
|
||||
/**
|
||||
* Cette interface permet à un {@link ByteBuffer} de sérialiser sous forme de
|
||||
* données binaire
|
||||
* les attributs de la classe courante.<br/>
|
||||
* <br/>
|
||||
* Les classes concrètes implémentant cette interface doivent avoir un
|
||||
* constructeur vide, utilisé
|
||||
* lors de la désérialisation
|
||||
*
|
||||
*/
|
||||
public interface ByteSerializable {
|
||||
|
||||
public void serializeToByteBuffer(ByteBuffer buffer);
|
||||
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer);
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet30ClientDeclareProcess extends PacketClient {
|
||||
|
||||
private String processName;
|
||||
private String type;
|
||||
|
||||
public Packet30ClientDeclareProcess() {
|
||||
super((byte)0x30);
|
||||
}
|
||||
|
||||
public void setProcessName(String pN) {
|
||||
processName = pN;
|
||||
}
|
||||
|
||||
public void setType(String t) {
|
||||
type = t;
|
||||
}
|
||||
|
||||
public String getProcessName() { return processName; }
|
||||
public String getType() { return type; }
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(processName);
|
||||
buffer.putString(type);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
processName = buffer.getString();
|
||||
type = buffer.getString();
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet31ClientClose extends PacketClient {
|
||||
|
||||
public Packet31ClientClose() {
|
||||
super((byte)0x31);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.util.network.packet.packets.core_slave.PacketB4ServerProcessQuery.QueryType;
|
||||
|
||||
public class Packet32ClientProcessQueryResponse extends PacketClient {
|
||||
|
||||
private QueryType type;
|
||||
private int queryId;
|
||||
private byte[] responseData = null;
|
||||
|
||||
|
||||
public Packet32ClientProcessQueryResponse() {
|
||||
super((byte)0x32);
|
||||
}
|
||||
|
||||
public QueryType getType() { return type; }
|
||||
public int getQueryId() { return queryId; }
|
||||
public byte[] getResponseData() { return responseData; }
|
||||
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putInt(type.ordinal());
|
||||
buffer.putInt(queryId);
|
||||
buffer.putSizedByteArray(responseData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
type = QueryType.values()[buffer.getInt()];
|
||||
queryId = buffer.getInt();
|
||||
responseData = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static Packet32ClientProcessQueryResponse destroyResponse(int queryId) {
|
||||
Packet32ClientProcessQueryResponse q = new Packet32ClientProcessQueryResponse();
|
||||
q.type = QueryType.DESTROY;
|
||||
q.queryId = queryId;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static Packet32ClientProcessQueryResponse isAliveResponse(int queryId, boolean resp) {
|
||||
Packet32ClientProcessQueryResponse q = new Packet32ClientProcessQueryResponse();
|
||||
q.type = QueryType.IS_ALIVE;
|
||||
q.queryId = queryId;
|
||||
q.responseData = new byte[] {(byte)(resp ? 1 : 0)};
|
||||
return q;
|
||||
}
|
||||
|
||||
public static Packet32ClientProcessQueryResponse exitStatusResponse(int queryId, int resp) {
|
||||
Packet32ClientProcessQueryResponse q = new Packet32ClientProcessQueryResponse();
|
||||
q.type = QueryType.EXIT_STATUS;
|
||||
q.queryId = queryId;
|
||||
q.responseData = new ByteBuffer(4, CHARSET).putInt(resp).array();
|
||||
return q;
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketB0ServerClose extends PacketServer {
|
||||
|
||||
public PacketB0ServerClose() {
|
||||
super((byte)0xB0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketB1ServerProcessDeclarationConfirm extends PacketServer {
|
||||
|
||||
private String serverName;
|
||||
|
||||
public PacketB1ServerProcessDeclarationConfirm() {
|
||||
super((byte)0xB1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
serverName = buffer.getString();
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setProcessName(String name) {
|
||||
serverName = name;
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketB2ServerConnectSuccess extends PacketServer {
|
||||
|
||||
public PacketB2ServerConnectSuccess() {
|
||||
super((byte)0xB2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketB3ServerProcessInput extends PacketServer {
|
||||
|
||||
private String serverName;
|
||||
private byte[] dataToSend;
|
||||
|
||||
public PacketB3ServerProcessInput() {
|
||||
super((byte)0xB3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(serverName);
|
||||
buffer.putSizedByteArray(dataToSend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
serverName = buffer.getString();
|
||||
dataToSend = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public byte[] getDataToSend() {
|
||||
return dataToSend;
|
||||
}
|
||||
|
||||
public void setDataToSend(byte[] dataToSend) {
|
||||
this.dataToSend = dataToSend;
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.util.RandomUtil;
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketB4ServerProcessQuery extends PacketServer {
|
||||
|
||||
private String processName;
|
||||
private QueryType type;
|
||||
private int queryId = RandomUtil.rand.nextInt();
|
||||
private byte[] queryData = null;
|
||||
|
||||
|
||||
public PacketB4ServerProcessQuery() {
|
||||
super((byte)0xB4);
|
||||
}
|
||||
|
||||
public String getProcessName() { return processName; }
|
||||
public QueryType getType() { return type; }
|
||||
public int getQueryId() { return queryId; }
|
||||
public byte[] getQueryData() { return queryData; }
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putInt(type.ordinal());
|
||||
buffer.putInt(queryId);
|
||||
buffer.putSizedByteArray(queryData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
type = QueryType.values()[buffer.getInt()];
|
||||
queryId = buffer.getInt();
|
||||
queryData = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static PacketB4ServerProcessQuery startQuery(String processName) {
|
||||
PacketB4ServerProcessQuery q = new PacketB4ServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.START;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketB4ServerProcessQuery destroyQuery(String processName, boolean wait) {
|
||||
PacketB4ServerProcessQuery q = new PacketB4ServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.DESTROY;
|
||||
q.queryData = new byte[] {(byte)(wait ? 1 : 0)};
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketB4ServerProcessQuery isAliveQuery(String processName) {
|
||||
PacketB4ServerProcessQuery q = new PacketB4ServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.IS_ALIVE;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketB4ServerProcessQuery exitStatusQuery(String processName) {
|
||||
PacketB4ServerProcessQuery q = new PacketB4ServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.EXIT_STATUS;
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
public enum QueryType {
|
||||
START, DESTROY, IS_ALIVE, EXIT_STATUS;
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet50ClientAuthenticate extends PacketClient {
|
||||
|
||||
private String password;
|
||||
private String additionalData = "";
|
||||
|
||||
public Packet50ClientAuthenticate() {
|
||||
super((byte)0x50);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(password);
|
||||
buffer.putString(additionalData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
password = buffer.getString();
|
||||
additionalData = buffer.getString();
|
||||
}
|
||||
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String p) { password = p; }
|
||||
|
||||
public String getAdditionalData() { return additionalData; }
|
||||
public void setAdditionalData(String data) { additionalData = data; }
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet51ClientLogRecord extends PacketClient {
|
||||
|
||||
private long time;
|
||||
private String level;
|
||||
private String threadName;
|
||||
private String message;
|
||||
private String throwable;
|
||||
|
||||
public Packet51ClientLogRecord() {
|
||||
super((byte)0x51);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
time = buffer.getLong();
|
||||
level = buffer.getString();
|
||||
threadName = buffer.getString();
|
||||
message = buffer.getString();
|
||||
throwable = buffer.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putLong(time);
|
||||
buffer.putString(level);
|
||||
buffer.putString(threadName);
|
||||
buffer.putString(message);
|
||||
buffer.putString(throwable);
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getThreadName() {
|
||||
return threadName;
|
||||
}
|
||||
|
||||
public void setThreadName(String threadName) {
|
||||
this.threadName = threadName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getThrowable() {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
public void setThrowable(String throwable) {
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.global;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketD0ServerException extends PacketServer {
|
||||
|
||||
private String exception;
|
||||
|
||||
public PacketD0ServerException() {
|
||||
super((byte)0xD0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
exception = buffer.getString();
|
||||
}
|
||||
|
||||
|
||||
public void setException(Throwable t) {
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
exception = sw.toString();
|
||||
}
|
||||
|
||||
public String getExceptionString() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketD1ServerCantAuthenticate extends PacketServer {
|
||||
|
||||
public PacketD1ServerCantAuthenticate() {
|
||||
super((byte)0xD1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketD2ServerCommand extends PacketServer {
|
||||
|
||||
private String command;
|
||||
private boolean async;
|
||||
private boolean returnResult;
|
||||
|
||||
public PacketD2ServerCommand() {
|
||||
super((byte)0xD2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(command);
|
||||
buffer.putByte((byte) (async ? 1 : 0));
|
||||
buffer.putByte((byte) (returnResult ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
command = buffer.getString();
|
||||
async = buffer.getByte() != 0;
|
||||
returnResult = buffer.getByte() != 0;
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.web;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet00ClientWebRequest extends PacketClient {
|
||||
|
||||
private String password;
|
||||
private String jsonData;
|
||||
|
||||
public Packet00ClientWebRequest() {
|
||||
super((byte)0x00);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(password);
|
||||
buffer.putString(jsonData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
password = buffer.getString();
|
||||
jsonData = buffer.getString();
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getJsonData() {
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public void setJsonData(String jsonData) {
|
||||
this.jsonData = jsonData;
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package fr.pandacube.util.network.packet.packets.web;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class Packet80ServerWebResponse extends PacketServer {
|
||||
|
||||
private String jsonData;
|
||||
|
||||
public Packet80ServerWebResponse() {
|
||||
super((byte)0x80);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(jsonData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
jsonData = buffer.getString();
|
||||
}
|
||||
|
||||
public String getJsonData() {
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public void setJsonData(String jsonData) {
|
||||
this.jsonData = jsonData;
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package fr.pandacube.util.network.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import fr.pandacube.util.network.server.TCPServer.TCPServerClientConnection;
|
||||
|
||||
public class BandwidthCalculation {
|
||||
|
||||
private List<PacketStat> packetHistory = new LinkedList<>();
|
||||
|
||||
public synchronized void addPacket(TCPServerClientConnection co, boolean in, long size) {
|
||||
packetHistory.add(new PacketStat(co, in, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instant bandwith in byte/s
|
||||
*
|
||||
* @param input true if getting input bw, false if getting output, null if
|
||||
* getting input + output
|
||||
* @param co
|
||||
* @return
|
||||
*/
|
||||
public synchronized long getBandWidth(Boolean input, TCPServerClientConnection co) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Iterator<PacketStat> it = packetHistory.iterator();
|
||||
long sum = 0;
|
||||
while (it.hasNext()) {
|
||||
PacketStat el = it.next();
|
||||
if (el.time < currentTime - 1000) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
if (input != null && el.input != input.booleanValue()) continue;
|
||||
if (co != null && !co.equals(el.connection)) continue;
|
||||
sum += el.packetSize;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private class PacketStat {
|
||||
public final long time;
|
||||
public final long packetSize;
|
||||
public final boolean input;
|
||||
public final TCPServerClientConnection connection;
|
||||
|
||||
public PacketStat(TCPServerClientConnection co, boolean input, long size) {
|
||||
time = System.currentTimeMillis();
|
||||
packetSize = size;
|
||||
this.input = input;
|
||||
connection = co;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,355 +0,0 @@
|
||||
package fr.pandacube.util.network.server;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.javatuples.Pair;
|
||||
|
||||
import fr.pandacube.Pandacube;
|
||||
import fr.pandacube.util.Log;
|
||||
import fr.pandacube.util.network.packet.Packet;
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.packet.PacketException;
|
||||
import fr.pandacube.util.network.packet.PacketServer;
|
||||
import fr.pandacube.util.network.packet.ResponseCallback;
|
||||
import fr.pandacube.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.util.network.packet.packets.global.PacketD0ServerException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
public class TCPServer extends Thread implements Closeable {
|
||||
private static AtomicInteger connectionCounterId = new AtomicInteger(0);
|
||||
|
||||
private ServerSocket socket;
|
||||
private TCPServerListener listener;
|
||||
private String socketName;
|
||||
|
||||
private List<TCPServerClientConnection> clients = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
public final BandwidthCalculation bandwidthCalculation = new BandwidthCalculation();
|
||||
|
||||
public TCPServer(int port, String sckName, TCPServerListener l) throws IOException {
|
||||
super("TCPSv " + sckName);
|
||||
setDaemon(true);
|
||||
if (port <= 0 || port > 65535) throw new IllegalArgumentException("le numéro de port est invalide");
|
||||
socket = new ServerSocket();
|
||||
socket.setReceiveBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setPerformancePreferences(0, 1, 0);
|
||||
socket.bind(new InetSocketAddress(port));
|
||||
listener = l;
|
||||
try {
|
||||
listener.onSocketOpen(this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onSocketOpen()", e);
|
||||
}
|
||||
socketName = sckName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
Socket socketClient = socket.accept();
|
||||
socketClient.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socketClient.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
|
||||
try {
|
||||
TCPServerClientConnection co = new TCPServerClientConnection(socketClient,
|
||||
connectionCounterId.getAndIncrement());
|
||||
clients.add(co);
|
||||
co.start();
|
||||
} catch (IOException e) {
|
||||
Log.getLogger().log(Level.SEVERE, "Connexion impossible avec " + socketClient.getInetAddress());
|
||||
}
|
||||
}
|
||||
} catch(SocketException e) {
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Plus aucune connexion ne peux être acceptée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public class TCPServerClientConnection extends Thread {
|
||||
private Socket cSocket;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
private SocketAddress address;
|
||||
private TCPServerConnectionOutputThread outThread;
|
||||
private List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacks = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacksAvoidListener = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
|
||||
public TCPServerClientConnection(Socket s, int coId) throws IOException {
|
||||
super("TCPSv " + socketName + " Conn#" + coId + " In");
|
||||
setDaemon(true);
|
||||
cSocket = s;
|
||||
in = cSocket.getInputStream();
|
||||
out = cSocket.getOutputStream();
|
||||
address = new InetSocketAddress(cSocket.getInetAddress(), cSocket.getPort());
|
||||
try {
|
||||
listener.onClientConnect(TCPServer.this, this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onClientConnect()", e);
|
||||
}
|
||||
outThread = new TCPServerConnectionOutputThread(coId);
|
||||
outThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
byte[] code = new byte[1];
|
||||
while (!cSocket.isClosed() && in.read(code) != -1) {
|
||||
byte[] sizeB = new byte[4];
|
||||
if (in.read(sizeB) != 4) throw new IOException("Socket " + address + " closed");
|
||||
|
||||
int size = new ByteBuffer(sizeB, Packet.CHARSET).getInt();
|
||||
|
||||
byte[] content = new byte[size];
|
||||
|
||||
forceReadBytes(content);
|
||||
|
||||
byte[] packetData = new ByteBuffer(1 + 4 + size, Packet.CHARSET).putByteArray(code).putByteArray(sizeB)
|
||||
.putByteArray(content).array();
|
||||
|
||||
bandwidthCalculation.addPacket(this, true, packetData.length);
|
||||
|
||||
try {
|
||||
Packet p = Packet.constructPacket(packetData);
|
||||
|
||||
if (!(p instanceof PacketClient))
|
||||
throw new PacketException(p.getClass().getCanonicalName() + " is not an instanceof PacketClient");
|
||||
|
||||
PacketClient pc = (PacketClient) p;
|
||||
|
||||
boolean oneCallbackExecuted = executeCallbacks(pc, callbacksAvoidListener);
|
||||
|
||||
if (!oneCallbackExecuted)
|
||||
listener.onPacketReceive(TCPServer.this, this, pc);
|
||||
|
||||
executeCallbacks(pc, callbacks);
|
||||
|
||||
} catch (Throwable e) {
|
||||
Log.severe("Exception while handling packet. This exception will be sent to the client with PacketServerException packet.", e);
|
||||
PacketD0ServerException packet = new PacketD0ServerException();
|
||||
packet.setException(e);
|
||||
send(packet);
|
||||
if (e instanceof InterruptedException || e instanceof Error)
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
} catch(SocketException e) {
|
||||
} catch (Exception e) {
|
||||
Log.severe("Closing connection " + address, e);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
private boolean executeCallbacks(PacketClient pc, List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacks) {
|
||||
boolean executedOne = false;
|
||||
synchronized (callbacks) {
|
||||
for(Iterator<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> it = callbacks.iterator(); it.hasNext();) {
|
||||
Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>> c = it.next();
|
||||
try {
|
||||
if (c.getValue0().test(pc)) {
|
||||
it.remove();
|
||||
c.getValue1().call(pc);
|
||||
executedOne = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while executing callback", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return executedOne;
|
||||
}
|
||||
|
||||
public void send(PacketServer p) {
|
||||
outThread.addPacket(p);
|
||||
}
|
||||
|
||||
public void sendAndGetResponse(PacketServer packet, Predicate<PacketClient> responseCondition, ResponseCallback<PacketClient> callback, boolean avoidListener) {
|
||||
Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>> p = new Pair<>(responseCondition, callback);
|
||||
if (avoidListener)
|
||||
callbacksAvoidListener.add(p);
|
||||
else
|
||||
callbacks.add(p);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packet the packet to send
|
||||
* @param responseCondition {@link Predicate} that check each received packet to know which
|
||||
* is the expected one as a response of the sended packet.
|
||||
* @param avoidListener
|
||||
* @param timeout
|
||||
* @return
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public PacketClient sendAndWaitForResponse(PacketServer packet, Predicate<PacketClient> responseCondition, boolean avoidListener, long timeout) throws InterruptedException {
|
||||
AtomicReference<PacketClient> pcStorage = new AtomicReference<>(null);
|
||||
synchronized (pcStorage) {
|
||||
sendAndGetResponse(packet, responseCondition, packetClient -> {
|
||||
synchronized (pcStorage) {
|
||||
pcStorage.set(packetClient);
|
||||
pcStorage.notifyAll();
|
||||
}
|
||||
}, true);
|
||||
|
||||
pcStorage.wait(timeout);
|
||||
return pcStorage.get();
|
||||
}
|
||||
}
|
||||
|
||||
private void forceReadBytes(byte[] buff) throws IOException {
|
||||
int pos = 0;
|
||||
do {
|
||||
int nbR = in.read(buff, pos, buff.length - pos);
|
||||
if (nbR == -1) throw new IOException("Can't read required amount of byte");
|
||||
pos += nbR;
|
||||
} while (pos < buff.length);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (cSocket.isClosed()) return;
|
||||
|
||||
try {
|
||||
listener.onClientDisconnect(TCPServer.this, this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onClientDisconnect()", e);
|
||||
}
|
||||
clients.remove(this);
|
||||
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
cSocket.close();
|
||||
if (!Thread.currentThread().equals(outThread)) send(new PacketServer((byte) 0) {
|
||||
@Override public void serializeToByteBuffer(ByteBuffer buffer) {}
|
||||
@Override public void deserializeFromByteBuffer(ByteBuffer buffer) {}
|
||||
});
|
||||
// provoque une exception dans le thread de sortie, et la
|
||||
// termine
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
|
||||
private class TCPServerConnectionOutputThread extends Thread {
|
||||
private BlockingQueue<PacketServer> packetQueue = new LinkedBlockingDeque<>();
|
||||
|
||||
public TCPServerConnectionOutputThread(int coId) {
|
||||
super("TCPSv " + socketName + " Conn#" + coId + " Out");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
private void addPacket(PacketServer packet) {
|
||||
packetQueue.add(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!cSocket.isClosed()) {
|
||||
PacketServer packet = packetQueue.poll(1, TimeUnit.SECONDS);
|
||||
byte[] data;
|
||||
if (packet != null) {
|
||||
try {
|
||||
data = packet.getFullSerializedPacket();
|
||||
bandwidthCalculation.addPacket(TCPServerClientConnection.this, false, data.length);
|
||||
out.write(data);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't send packet "+packet.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (InterruptedException|IOException e) {}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", cSocket).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
if (isClosed.get()) return;
|
||||
isClosed.set(true);
|
||||
|
||||
clients.forEach(el -> el.close());
|
||||
|
||||
try {
|
||||
listener.onSocketClose(this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onSocketClose()", e);
|
||||
}
|
||||
|
||||
socket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<TCPServerClientConnection> getClients() {
|
||||
synchronized (clients) {
|
||||
return new ArrayList<>(clients);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket).toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package fr.pandacube.util.network.server;
|
||||
|
||||
import fr.pandacube.util.network.packet.PacketClient;
|
||||
import fr.pandacube.util.network.server.TCPServer.TCPServerClientConnection;
|
||||
|
||||
public interface TCPServerListener {
|
||||
|
||||
public void onSocketOpen(TCPServer svConnection);
|
||||
|
||||
public void onClientConnect(TCPServer svConnection, TCPServerClientConnection clientConnection);
|
||||
|
||||
public void onPacketReceive(TCPServer svConnection, TCPServerClientConnection clientConnection,
|
||||
PacketClient packet);
|
||||
|
||||
public void onClientDisconnect(TCPServer svConnection, TCPServerClientConnection clientConnection);
|
||||
|
||||
public void onSocketClose(TCPServer svConnection);
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package fr.pandacube.util.network_api.client;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
@Deprecated
|
||||
public abstract class AbstractRequest {
|
||||
|
||||
private final String pass;
|
||||
|
@ -5,6 +5,7 @@ import java.io.PrintStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
@Deprecated
|
||||
public class NetworkAPISender {
|
||||
|
||||
public static ResponseAnalyser sendRequest(InetSocketAddress cible, AbstractRequest request) throws IOException {
|
||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
|
||||
@Deprecated
|
||||
public class ResponseAnalyser {
|
||||
/**
|
||||
* Indique si la requête s'est bien exécutée (l'entête de la réponse est
|
||||
|
@ -7,6 +7,7 @@ import java.net.Socket;
|
||||
|
||||
import fr.pandacube.util.Log;
|
||||
|
||||
@Deprecated
|
||||
public abstract class AbstractRequestExecutor {
|
||||
|
||||
public final String command;
|
||||
|
@ -1,14 +0,0 @@
|
||||
package fr.pandacube.util.network_api.server;
|
||||
|
||||
/**
|
||||
* Interface permettant de gérer l'exécution asynchrone d'un PacketExecutor.
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface NAPIExecutionHandler {
|
||||
|
||||
public void handleRun(Runnable executor);
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import java.net.ServerSocket;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Deprecated
|
||||
public class NetworkAPIListener implements Runnable {
|
||||
|
||||
private int port = 0;
|
||||
@ -12,7 +13,6 @@ public class NetworkAPIListener implements Runnable {
|
||||
private ServerSocket serverSocket;
|
||||
private HashMap<String, AbstractRequestExecutor> requestExecutors = new HashMap<>();
|
||||
private String name;
|
||||
private NAPIExecutionHandler nAPIExecutionHandler;
|
||||
|
||||
/**
|
||||
* Instencie le côté serveur du NetworkAPI
|
||||
@ -23,11 +23,10 @@ public class NetworkAPIListener implements Runnable {
|
||||
* @param peh PacketExecutionHandler permettant de prendre en charge
|
||||
* l'exécution asynchrone d'une requête reçu pas un client
|
||||
*/
|
||||
public NetworkAPIListener(String n, int p, String pa, NAPIExecutionHandler peh) {
|
||||
public NetworkAPIListener(String n, int p, String pa) {
|
||||
port = p;
|
||||
pass = pa;
|
||||
name = n;
|
||||
nAPIExecutionHandler = peh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,7 +45,9 @@ public class NetworkAPIListener implements Runnable {
|
||||
try {
|
||||
// réception des connexion client
|
||||
while (!serverSocket.isClosed()) {
|
||||
nAPIExecutionHandler.handleRun(new PacketExecutor(serverSocket.accept(), this));
|
||||
Thread t = new Thread(new PacketExecutor(serverSocket.accept(), this));
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import fr.pandacube.util.network_api.server.RequestAnalyser.BadRequestException;
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public class PacketExecutor implements Runnable {
|
||||
private Socket socket;
|
||||
private NetworkAPIListener networkAPIListener;
|
||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
|
||||
@Deprecated
|
||||
public class RequestAnalyser {
|
||||
|
||||
private NetworkAPIListener networkAPIListener;
|
||||
|
@ -2,6 +2,7 @@ package fr.pandacube.util.network_api.server;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
@Deprecated
|
||||
public class Response {
|
||||
public boolean good = true;
|
||||
public String data = "";
|
||||
|
@ -1,12 +0,0 @@
|
||||
package fr.pandacube.util.network_api.server;
|
||||
|
||||
public class ThreadNAPIExecutionHandler implements NAPIExecutionHandler {
|
||||
|
||||
@Override
|
||||
public void handleRun(Runnable executor) {
|
||||
Thread t = new Thread(executor);
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user