Skip to content
Permalink
Browse files

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
  • Loading branch information...
collinsmith committed Sep 12, 2019
1 parent 3b24c3a commit 7495aff11d9e3b2022b8c95bf4d8ce326d04df9d
@@ -1,8 +1,10 @@
include "ConnectionClosed.fbs";
include "QueryRealms.fbs";

namespace com.riiablo.net.packet.bnls;

union BNLSData {
ConnectionClosed,
QueryRealms,
}

@@ -0,0 +1,5 @@
namespace com.riiablo.net.packet.bnls;

table ConnectionClosed {
reason:string;
}
@@ -7,10 +7,12 @@
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.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 static void main(String[] args) {

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 void create() {
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());

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, "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();
}
}

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 void dispose() {
kill.set(true);
server.dispose();
try {
thread.join();
main.join();
} catch (Throwable ignored) {}
}

@@ -148,7 +155,21 @@ private void process(Socket socket, com.riiablo.net.packet.bnls.BNLS packet) thr
}
}

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 @@ private void QueryRealms(Socket socket) throws IOException {
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();
}
}
}
}
}
@@ -60,7 +60,12 @@ public void testQueryRealmsReq() throws IOException {
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:");

0 comments on commit 7495aff

Please sign in to comment.
You can’t perform that action at this time.