Added support for multiple client connections to BNLS

Added support for multiple client connections to BNLS
Added support for ConnectionClosed packet to politely close connection
This commit is contained in:
Collin Smith 2019-09-12 02:23:53 -07:00
parent 3b24c3a58b
commit 7495aff11d
4 changed files with 96 additions and 26 deletions

View File

@ -1,8 +1,10 @@
include "ConnectionClosed.fbs";
include "QueryRealms.fbs"; include "QueryRealms.fbs";
namespace com.riiablo.net.packet.bnls; namespace com.riiablo.net.packet.bnls;
union BNLSData { union BNLSData {
ConnectionClosed,
QueryRealms, QueryRealms,
} }

View File

@ -0,0 +1,5 @@
namespace com.riiablo.net.packet.bnls;
table ConnectionClosed {
reason:string;
}

View File

@ -7,10 +7,12 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net; import com.badlogic.gdx.Net;
import com.badlogic.gdx.backends.headless.HeadlessApplication; import com.badlogic.gdx.backends.headless.HeadlessApplication;
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration; import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.net.ServerSocket; import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.utils.BufferUtils; import com.badlogic.gdx.utils.BufferUtils;
import com.riiablo.net.packet.bnls.BNLSData; import com.riiablo.net.packet.bnls.BNLSData;
import com.riiablo.net.packet.bnls.ConnectionClosed;
import com.riiablo.net.packet.bnls.QueryRealms; import com.riiablo.net.packet.bnls.QueryRealms;
import com.riiablo.net.packet.bnls.Realm; import com.riiablo.net.packet.bnls.Realm;
@ -26,12 +28,14 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public class BNLS extends ApplicationAdapter { public class BNLS extends ApplicationAdapter {
private static final String TAG = "BNLS"; private static final String TAG = "BNLS";
private static final int PORT = 6110; private static final int PORT = 6110;
private static final int MAX_CLIENTS = 32;
public static void main(String[] args) { public static void main(String[] args) {
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
@ -40,9 +44,11 @@ public class BNLS extends ApplicationAdapter {
ServerSocket server; ServerSocket server;
ByteBuffer buffer; ByteBuffer buffer;
Thread thread; Thread main;
AtomicBoolean kill; AtomicBoolean kill;
Thread cli; Thread cli;
ThreadGroup clientThreads;
CopyOnWriteArrayList<Client> CLIENTS = new CopyOnWriteArrayList<>();
private static final String[][] REALMS = new String[][] { private static final String[][] REALMS = new String[][] {
{"localhost", "U.S. West"}, {"localhost", "U.S. West"},
@ -65,42 +71,43 @@ public class BNLS extends ApplicationAdapter {
Gdx.app.error(TAG, e.getMessage(), e); Gdx.app.error(TAG, e.getMessage(), e);
} }
clientThreads = new ThreadGroup("BNLSClients");
Gdx.app.log(TAG, "Starting server..."); Gdx.app.log(TAG, "Starting server...");
server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null); server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null);
buffer = BufferUtils.newByteBuffer(4096); buffer = BufferUtils.newByteBuffer(4096);
kill = new AtomicBoolean(false); kill = new AtomicBoolean(false);
thread = new Thread(new Runnable() { main = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
while (!kill.get()) { while (!kill.get()) {
Socket socket = null; Gdx.app.log(TAG, "waiting...");
try { Socket socket = server.accept(null);
Gdx.app.log(TAG, "waiting..."); Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress());
socket = server.accept(null); if (CLIENTS.size() >= MAX_CLIENTS) {
Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress()); try {
ConnectionDenied(socket, "Server is Full");
} catch (Throwable ignored) {
} finally {
socket.dispose();
}
} else {
new Client(socket).start();
}
}
buffer.mark(); Gdx.app.log(TAG, "killing child threads...");
ReadableByteChannel in = Channels.newChannel(socket.getInputStream()); for (Client client : CLIENTS) {
in.read(buffer); if (client != null) {
buffer.limit(buffer.position()); client.kill.set(true);
buffer.reset();
com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer);
Gdx.app.log(TAG, "packet type " + BNLSData.name(packet.dataType()));
process(socket, packet);
} catch (Throwable t) {
Gdx.app.log(TAG, t.getMessage());
} finally {
Gdx.app.log(TAG, "closing socket...");
if (socket != null) socket.dispose();
} }
} }
Gdx.app.log(TAG, "killing thread..."); Gdx.app.log(TAG, "killing thread...");
} }
}); });
thread.setName("BNLS"); main.setName("BNLS");
thread.start(); main.start();
cli = new Thread(new Runnable() { cli = new Thread(new Runnable() {
@Override @Override
@ -134,7 +141,7 @@ public class BNLS extends ApplicationAdapter {
kill.set(true); kill.set(true);
server.dispose(); server.dispose();
try { try {
thread.join(); main.join();
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
} }
@ -148,7 +155,21 @@ public class BNLS extends ApplicationAdapter {
} }
} }
private void QueryRealms(Socket socket) throws IOException { private boolean ConnectionDenied(Socket socket, String reason) throws IOException {
FlatBufferBuilder builder = new FlatBufferBuilder();
int reasonOffset = builder.createString(reason);
int connectionDeniedId = ConnectionClosed.createConnectionClosed(builder, reasonOffset);
int id = com.riiablo.net.packet.bnls.BNLS.createBNLS(builder, BNLSData.ConnectionClosed, connectionDeniedId);
builder.finish(id);
ByteBuffer data = builder.dataBuffer();
OutputStream out = socket.getOutputStream();
WritableByteChannel channel = Channels.newChannel(out);
channel.write(data);
return true;
}
private boolean QueryRealms(Socket socket) throws IOException {
FlatBufferBuilder builder = new FlatBufferBuilder(); FlatBufferBuilder builder = new FlatBufferBuilder();
int[] realms = new int[REALMS.length]; int[] realms = new int[REALMS.length];
@ -171,5 +192,42 @@ public class BNLS extends ApplicationAdapter {
WritableByteChannel channel = Channels.newChannel(out); WritableByteChannel channel = Channels.newChannel(out);
channel.write(data); channel.write(data);
Gdx.app.log(TAG, "returning realms list..."); Gdx.app.log(TAG, "returning realms list...");
return false;
}
static String generateClientName() {
return String.format("Client-%08X", MathUtils.random(1, Integer.MAX_VALUE - 1));
}
private class Client extends Thread {
Socket socket;
AtomicBoolean kill = new AtomicBoolean(false);
Client(Socket socket) {
super(clientThreads, generateClientName());
this.socket = socket;
}
@Override
public void run() {
while (!kill.get()) {
try {
buffer.mark();
ReadableByteChannel in = Channels.newChannel(socket.getInputStream());
in.read(buffer);
buffer.limit(buffer.position());
buffer.reset();
com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer);
Gdx.app.log(TAG, "packet type " + BNLSData.name(packet.dataType()));
process(socket, packet);
} catch (Throwable t) {
Gdx.app.log(TAG, t.getMessage());
} finally {
Gdx.app.log(TAG, "closing socket...");
if (socket != null) socket.dispose();
}
}
}
} }
} }

View File

@ -60,7 +60,12 @@ public class BNLSTest {
buffer.rewind(); buffer.rewind();
com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer); com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer);
Gdx.app.log(TAG, "packet " + packet.dataType()); Gdx.app.log(TAG, "packet " + BNLSData.name(packet.dataType()));
if (packet.dataType() == BNLSData.ConnectionClosed) {
Gdx.app.log(TAG, "connection closed.");
return;
}
QueryRealms qr = (QueryRealms) packet.data(new QueryRealms()); QueryRealms qr = (QueryRealms) packet.data(new QueryRealms());
Gdx.app.log(TAG, "realms:"); Gdx.app.log(TAG, "realms:");