I think the problem has something to do with my lack of multithreading
Yes and no. Threads aren't required by far to do networking. They were strictly forbidden up until appearance of Java.
Unfortunately, the default sockets provided by JDK are somewhat tedious to use without due to the way handlers are designed. The alternative, NIO, offers top notch networking, but is a big pain to use.
The usual solution is this:
class ClientHandler {
List<String> toSend;
List<String> received;
Socket s;
public ClientHandler(Socket s) {
this.s = s;
}
public void update() {
int readable = s.getInputStream().available();;
if (readable > 0) {
byte b = new byte[readable];
s.getInputStream().read(b);
received.add(new String(b));
}
int writable = s.getOutputStream().available();
if (writable > 0) {
// same as above
}
}
}
...
List<ClientHandler> handlers;
s = new ServerSocket(100);
s.setSoTimeout(5); // wait 5 milliseconds max
while (running) {
try {
ClientHandler ch = new ClientHandler(s.accept());
handlers.add(ch);
} catch (TimeoutException te) {
// no new connections in last 5 milliseconds
}
for (ClientHandler ch : handlers) {
ch.update();
// check if each client received some data during last update
// remove it from their 'received' and do something useful with it
}
}The server repeatedly waits 5 milliseconds for new connections then gives up to allow send/receive code to run.
To send data to specific client, put it into respective ClientHandler's toSend list.
When a socket receives data, it will be available in their 'received' list.
There is no error handling above. Clients are also not checked for disconnection. See the read/write and related documentation on socket and input/output streams to determine how exactly to determine those.