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";
namespace com.riiablo.net.packet.bnls;
union BNLSData {
ConnectionClosed,
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.backends.headless.HeadlessApplication;
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.utils.BufferUtils;
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.Realm;
@ -26,12 +28,14 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class BNLS extends ApplicationAdapter {
private static final String TAG = "BNLS";
private static final int PORT = 6110;
private static final int MAX_CLIENTS = 32;
public static void main(String[] args) {
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
@ -40,9 +44,11 @@ public class BNLS extends ApplicationAdapter {
ServerSocket server;
ByteBuffer buffer;
Thread thread;
Thread main;
AtomicBoolean kill;
Thread cli;
ThreadGroup clientThreads;
CopyOnWriteArrayList<Client> CLIENTS = new CopyOnWriteArrayList<>();
private static final String[][] REALMS = new String[][] {
{"localhost", "U.S. West"},
@ -65,42 +71,43 @@ public class BNLS extends ApplicationAdapter {
Gdx.app.error(TAG, e.getMessage(), e);
}
clientThreads = new ThreadGroup("BNLSClients");
Gdx.app.log(TAG, "Starting server...");
server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null);
buffer = BufferUtils.newByteBuffer(4096);
kill = new AtomicBoolean(false);
thread = new Thread(new Runnable() {
main = new Thread(new Runnable() {
@Override
public void run() {
while (!kill.get()) {
Socket socket = null;
try {
Gdx.app.log(TAG, "waiting...");
socket = server.accept(null);
Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress());
Gdx.app.log(TAG, "waiting...");
Socket socket = server.accept(null);
Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress());
if (CLIENTS.size() >= MAX_CLIENTS) {
try {
ConnectionDenied(socket, "Server is Full");
} catch (Throwable ignored) {
} finally {
socket.dispose();
}
} else {
new Client(socket).start();
}
}
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();
Gdx.app.log(TAG, "killing child threads...");
for (Client client : CLIENTS) {
if (client != null) {
client.kill.set(true);
}
}
Gdx.app.log(TAG, "killing thread...");
}
});
thread.setName("BNLS");
thread.start();
main.setName("BNLS");
main.start();
cli = new Thread(new Runnable() {
@Override
@ -134,7 +141,7 @@ public class BNLS extends ApplicationAdapter {
kill.set(true);
server.dispose();
try {
thread.join();
main.join();
} 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();
int[] realms = new int[REALMS.length];
@ -171,5 +192,42 @@ public class BNLS extends ApplicationAdapter {
WritableByteChannel channel = Channels.newChannel(out);
channel.write(data);
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();
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());
Gdx.app.log(TAG, "realms:");