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;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Array8Bit implements ByteSerializable {
|
public class Array8Bit {
|
||||||
|
|
||||||
public static final int BIT_COUNT = Byte.SIZE;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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 java.nio.ByteBuffer buff;
|
||||||
private Charset charset;
|
|
||||||
|
|
||||||
public ByteBuffer(Charset c) {
|
public ByteBuffer() {
|
||||||
this(16, c);
|
this(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBuffer(int initSize, Charset c) {
|
public ByteBuffer(int initSize) {
|
||||||
buff = java.nio.ByteBuffer.allocate(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));
|
* Create a ByteBuffer that is initially <b>backed</b> by the provided byte array.
|
||||||
charset = c;
|
* 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) {
|
private void askForBufferExtension(int needed) {
|
||||||
@ -32,9 +37,12 @@ public class ByteBuffer implements Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This clone method also clone the underlying array.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer clone() {
|
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) {
|
if (s == null) {
|
||||||
return putInt(-1);
|
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() {
|
public String getString() {
|
||||||
byte[] binaryString = getSizedByteArray();
|
byte[] binaryString = getSizedByteArray();
|
||||||
return (binaryString == null) ? null : new String(binaryString, charset);
|
return (binaryString == null) ? null : new String(binaryString, Pandacube.NETWORK_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,25 +250,10 @@ public class ByteBuffer implements Cloneable {
|
|||||||
return this;
|
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.
|
* @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();
|
int size = getInt();
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
return null;
|
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;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public abstract class AbstractRequest {
|
public abstract class AbstractRequest {
|
||||||
|
|
||||||
private final String pass;
|
private final String pass;
|
||||||
|
@ -5,6 +5,7 @@ import java.io.PrintStream;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class NetworkAPISender {
|
public class NetworkAPISender {
|
||||||
|
|
||||||
public static ResponseAnalyser sendRequest(InetSocketAddress cible, AbstractRequest request) throws IOException {
|
public static ResponseAnalyser sendRequest(InetSocketAddress cible, AbstractRequest request) throws IOException {
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class ResponseAnalyser {
|
public class ResponseAnalyser {
|
||||||
/**
|
/**
|
||||||
* Indique si la requête s'est bien exécutée (l'entête de la réponse est
|
* 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;
|
import fr.pandacube.util.Log;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public abstract class AbstractRequestExecutor {
|
public abstract class AbstractRequestExecutor {
|
||||||
|
|
||||||
public final String command;
|
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.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class NetworkAPIListener implements Runnable {
|
public class NetworkAPIListener implements Runnable {
|
||||||
|
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
@ -12,7 +13,6 @@ public class NetworkAPIListener implements Runnable {
|
|||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
private HashMap<String, AbstractRequestExecutor> requestExecutors = new HashMap<>();
|
private HashMap<String, AbstractRequestExecutor> requestExecutors = new HashMap<>();
|
||||||
private String name;
|
private String name;
|
||||||
private NAPIExecutionHandler nAPIExecutionHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instencie le côté serveur du NetworkAPI
|
* Instencie le côté serveur du NetworkAPI
|
||||||
@ -23,11 +23,10 @@ public class NetworkAPIListener implements Runnable {
|
|||||||
* @param peh PacketExecutionHandler permettant de prendre en charge
|
* @param peh PacketExecutionHandler permettant de prendre en charge
|
||||||
* l'exécution asynchrone d'une requête reçu pas un client
|
* 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;
|
port = p;
|
||||||
pass = pa;
|
pass = pa;
|
||||||
name = n;
|
name = n;
|
||||||
nAPIExecutionHandler = peh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -46,7 +45,9 @@ public class NetworkAPIListener implements Runnable {
|
|||||||
try {
|
try {
|
||||||
// réception des connexion client
|
// réception des connexion client
|
||||||
while (!serverSocket.isClosed()) {
|
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) {}
|
} catch (IOException e) {}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import fr.pandacube.util.network_api.server.RequestAnalyser.BadRequestException;
|
|||||||
* @author Marc Baloup
|
* @author Marc Baloup
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class PacketExecutor implements Runnable {
|
public class PacketExecutor implements Runnable {
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private NetworkAPIListener networkAPIListener;
|
private NetworkAPIListener networkAPIListener;
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class RequestAnalyser {
|
public class RequestAnalyser {
|
||||||
|
|
||||||
private NetworkAPIListener networkAPIListener;
|
private NetworkAPIListener networkAPIListener;
|
||||||
|
@ -2,6 +2,7 @@ package fr.pandacube.util.network_api.server;
|
|||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class Response {
|
public class Response {
|
||||||
public boolean good = true;
|
public boolean good = true;
|
||||||
public String data = "";
|
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