1
\$\begingroup\$

I'm working on creating a multiplayer game, with the client in ActionScript and the server written in Java. I'm using XMLSocket in the client to connect to the server. The server code looks something like:

ServerSocket server = new ServerSocket(port);

while(true)
{
    Socket client = server.accept();
    ClientHandlingProcess child = new ClientHandlingProcess(client);
    child.start(); // starts new thread for client
}

The client handling process looks something like:

while(true)
{
    byte[] arr = new byte[1024];
    if(socket.getInputStream().read(arr) == -1)
    {
        break;
    }

    handleInputMethod(arr);

    while(!message_queue.isEmpty())
    {
        socket.getOutputStream().write(message_queue.get(0).getBytes());
        message_queue.remove(0);
    }
}

The issue is that the read method is blocking, meaning I can't send any data while I'm waiting for data to come in. What would be a good way to fix this? Have two processes: one for reading and one for writing, have the client send empty requests when it has no proper messages to send, or is there another way I can proceed?

\$\endgroup\$
1
  • \$\begingroup\$ The main takeaway from the below answers is that you should steer away from one (or more) thread per client, for scaleability reasons. Even today's most powerful quad core processors can only technically handle 8 simultaneous threads (four cores, two threads per core via hyperthreading); any more than that and there is an overhead for context switching between threads; essentially the processor juggling more threads than it can physically handle. Instead, look into non-blocking IO e.g. via Java's NIO library, or a 3rd party library. \$\endgroup\$
    – Ricket
    Apr 26, 2011 at 4:03

4 Answers 4

2
\$\begingroup\$

Never rely on a client to do ANYTHING to make your server run properly. If you do, then malicious users can attack your game simply by refusing to perform whatever is required of them.

Use non-blocking IO or threads for this task. Threads is fine if you have a small number of players (eg, < 10) and you are confident with passing data safely between threads. Otherwise, go for the non-blocking IO route.

\$\endgroup\$
4
  • \$\begingroup\$ A client not sending data will only block that client's thread, not in any way cause the server to run improperly. He is already using threads. \$\endgroup\$
    – Ricket
    Apr 25, 2011 at 17:37
  • \$\begingroup\$ So, someone can log in lots of clients and leave them all dormant, eating up memory. You must never leave your system at the mercy of clients because that's how denial of service attacks happen. \$\endgroup\$
    – Kylotan
    Apr 25, 2011 at 18:58
  • \$\begingroup\$ Would that not be the case for any server? \$\endgroup\$
    – Ricket
    Apr 26, 2011 at 3:57
  • 1
    \$\begingroup\$ Depends what kind of server it is, but the general theme is that the content of the server's response may depend on the client but the actual response must not - if the client doesn't submit the request in time, or submits an invalid request, the connection is dropped. Servers have to assume clients are malicious and malformed and not base their own control flow around the client. \$\endgroup\$
    – Kylotan
    Apr 26, 2011 at 22:10
2
\$\begingroup\$

You have basically these two options:

  • Use blocking IO with one reading thread per client
  • Use non blocking IO

The first option has the disadvantage that it does not scale for large number of clients.

While non blocking IO can be done by checking inputstream.available(), it may be a good idea to look at the New I/O API. Wikipedia has a good article on NIO with sample code. You can find additional information in the JavaDoc for the NIO package. And on a quick glance this tutorial seems helpful.

You might get away with doing smart things on the client side, such as sending a dummy message on every "turn". This, however, means that a rouge client can freeze your server. So you should not go into this direction.

\$\endgroup\$
2
  • \$\begingroup\$ He is already using blocking IO with one thread per client. \$\endgroup\$
    – Ricket
    Apr 25, 2011 at 17:36
  • \$\begingroup\$ @Ricket, added the word "reading" before "thread per client". \$\endgroup\$ Apr 25, 2011 at 17:40
1
\$\begingroup\$

Java's NIO package (as of Java6), provides support for non-blocking I/O only, via Selectors.

\$\endgroup\$
3
  • \$\begingroup\$ Is that the best option though? What is the advantage of this over sending dummy packets to the server? \$\endgroup\$
    – user2971
    Apr 24, 2011 at 16:12
  • \$\begingroup\$ Yes, NIO is the best option for a server handling many clients. One thread per client has a huge overhead for context switching. And as a solution to your current code, dummy packets would be very wasteful (a lot more incoming bandwidth that the server has to handle) and inconsistent. Each client's connection would then run at different rates depending on the client's connection speed and quality. Terrible idea. The read call should not block, whether you use the old non-blocking ways or restructure your server for NIO. \$\endgroup\$
    – Ricket
    Apr 25, 2011 at 17:40
  • \$\begingroup\$ @user6924 just to clarify the NIO was introduced in Java1.4 you can check it even in wiki \$\endgroup\$
    – Boro
    May 8, 2011 at 8:56
0
\$\begingroup\$

I agree with the other answers that NIO provides better results. However using NIO isn't that trivial. You will definitely learn something when you code the NIO network code yourself. Or you just use an asynch network package like netty or Apache Mina. I prefer netty, since I find it better documented. However if you understand one of them, you understand the other one as well.

But, if you want to keep the design you have, you can split the sending and receiving code. Create some "PlayerSession" or "PlayerConnection" Object that provides the methods for sending messages to the client and contains one thread that blocks on the read and handles input. Maybe you want to move the sending stuff to an own thread, just try it.

\$\endgroup\$

You must log in to answer this question.