2

I'm looking to write a small client-server-based text game that handles multiple client connections and persistently affects a game state. I'm wondering what the best way would be to handle multiple connects such that commands are processed in the order they arrive at the server.

Ideally I'm not looking to take advantage of multi-threading, at least on the command processing level. I would be okay with each client having a separate thread (in order to have blocking IO on each thread), as long as I could unify the processing in a single thread thereafter.

Since the only communication between the client and server will be text, I'm not sure how best to go about setting up the communication. If I chose blocking IO, how would I get queue the processing to occur in a single thread?

Alternatively, if I choose non-blocking IO and use a selector to query for when clients have written to the server, how can I get read a String of unknown/unlimited length without using a set-size ByteBuffer? Non-blocking also favours keeping the processing in a single thread as it can just read from the client connections as and when they send new data. However, when I tried to implement it with read/writeUTF I came up against the IllegalBlockingModeException heh.

Any answers to the questions or suggestions on how to do this in a way I haven't mentioned would be sincerely appreciated! I'm fairly new to clients and servers so I don't know whether java.io or java.nio would be most appropriate.

Sorry for the convoluted question. I think I ran away with myself.

Sam
  • 163
  • 1
  • 7

2 Answers2

1

i'm no expert in sever client systems but I'll share a couple of tips

Depending on your need you could simply set up a Tomcat server and do http request, its fairly straight forwards and of course all in Java.

the downside is that the request might be a bit slow.

The Second option you can check out is RMI.

The concept is simple you connect to another computer and when that is done you call methods on the other computer from a local object in you code.

http://java.sun.com/developer/onlineTraining/rmi/RMI.html

it might look a bit complicated (and sometimes debbuging a stack through multiple computer is a bit tricky) but I recommend because it keeps your code clear.

Finally you can try sockets but there your on your own :P

Jason Rogers
  • 19,194
  • 27
  • 79
  • 112
  • I've had a little look at RMI over the past week or so (incidentally as part of coursework I have to do), and I don't think that's quite what I'm looking for as a solution here. Sockets are definitely the way I want to go here, but thanks for the reply. :) – Sam Apr 18 '11 at 12:08
1

Opinions differ, but I'd definitely go with a single thread per client. The communication to the single processing thread could then go via a LinkedBlockingQueue, or just a synchronized LinkedList.

Something like this on the per-client thread:

public class Client implements Runnable, ResponseOutput {

    private final BufferedReader br;
    private final PrintWriter pw;

    public Client(Socket s) {
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        pw = new PrintWriter(s.getOutputStream());
    }

    // defined by the ResponseOutput interface
    public void sendReply(String reply) {
        pw.println(reply);
    }

    public void run() {
        try {
            while (true) {
                String s = br.readLine();
                if (s==null)
                    break;
                Processor.queue(new Processor.InputItem(this, s));
            }
        } catch (IOException ioe) {
            ... error handling ...
        }
    }
}

Then this for the processing:

public class Processor implements Runnable {
    static public class InputItem {
        final ResponseOutput client;
        final String command;

        public InputItem(ResponseOutput client, String command) {
            this.client = client;
            this.command = command;
        }
    }

    static private Processor instance;
    static public void queue(InputItem item) {
        instance.commandQueue.add(item);
    }

    private BlockingQueue<InputItem> commandQueue;

    public void run() {
        try {
            while (true) {
                InputItem item = commandQueue.take();
                String reply = doStuff(item.command);
                item.client.sendReply(reply);
            }
        } catch (InterruptedException ie) {
            ... error handling ....
        }
    }
}

Within the InputItem class, you can also include a reference to any game state that needs updating. Since there's only the processing thread changing it, you get to do that without any synchronization.

Jon Bright
  • 13,388
  • 3
  • 31
  • 46
  • That looks fab! I'll try implementing it in a little while to see if it solves all of my problems, but at the moment it looks very much like it will. Brilliant! – Sam Apr 18 '11 at 12:08
  • Sam, let me know if you hit any problems, I'll try and update my answer to deal with them. – Jon Bright Apr 18 '11 at 12:12
  • Hi Jon, I'm getting a NullPointerException from "Input item = commandQueue.take()", which seems to be from commandQueue. I have the following code... `while (true) { Input item = commandQueue.take(); String reply = parseInput(item.command); item.reply(reply); }` Also, is there any way to have the server accept clients in a non-blocking manner? This is so that, server-side, I could initiate a shutdown without having to wait for a client to be accepted. `public void run() { while (!shutdown) { try { acceptClient(server.accept()); } catch (...) {} } }` – Sam Apr 18 '11 at 13:58
  • Ah heh, my bad! I needed to add `private BlockingQueue commandQueue = new LinkedBlockingQueue();` like you mentioned. That's sorted the first part. – Sam Apr 18 '11 at 14:14
  • Sam, you just need to add a method to the accepting thread that calls server.close(); - this will cause your accept() call to throw an IOException. – Jon Bright Apr 18 '11 at 14:30