Sludge-Like Network Performance

Hey everyone, so I’ve tried to program a two-player version of Tetris. Essentially, the server runs the game, the client sends move/rotate commands, and then the server sends back the board and piece data.

I’m using ObjectInputStreams and ObjectOutputStreams in TCP/IP. I’d do something like this:


Socket client;
ServerSocket ss;
ObjectOutputStream oos;
ObjectInputStream ois;
Transmitter transmitter;
Receiver receiver;

client = ss.accept();
                        
                        oos = new ObjectOutputStream(client.getOutputStream());
                        ois = new ObjectInputStream(client.getInputStream());

                        transmitter = new Transmitter(oos);
                        receiver = new Receiver(ois);

Transmitter Class:


package tetris.net;

import java.io.*;
import java.net.*;
import java.util.*;

public class Transmitter implements Runnable
{
      ObjectOutputStream oos;

      Vector<Object> vec;

      public boolean running;

      Thread th;

      public Transmitter (ObjectOutputStream oos)
      {
            this.oos = oos;

            vec = new Vector<Object>(50,10);

            running = true;

            th = new Thread(this);
            th.start();
      }

      public void run ()
      {
            while (running)
            {
                  while (!vec.isEmpty())
                  {
                        Object object = vec.get(0);

                        writeOut(object);

                        vec.remove(0);
                  }

                  try
                  {
                        th.sleep(15);
                  }

                  catch (InterruptedException ie) {}
            }
      }

      private void writeOut (Object object)
      {
            try
            {
                  oos.writeObject(object);
                  oos.reset();
            }

            catch (Exception e)
            {
                  System.out.println("Error on transmitter");
                  running = false;
            }
      }

      public void add (Object object)
      {
            vec.add(object);
      }

      public int getSize ()
      {
            return vec.size();
      }
}

Receiver Class:


package tetris.net;

import java.io.*;
import java.net.*;
import java.util.*;

public class Receiver implements Runnable
{
      ObjectInputStream ois;

      Vector<Object> vec;

      public boolean running;

      Thread th;

      public Receiver (ObjectInputStream ois)
      {
            this.ois = ois;

            vec = new Vector<Object>(50,10);

            running = true;

            th = new Thread(this);
            th.start();
      }

      public void run ()
      {
            while (running)
            {
                  readIn();
            }
      }

      private void readIn ()
      {
            Object object;

            try
            {
                  object = ois.readObject();
            }

            catch (Exception e)
            {
                  System.out.println("Error on receiver");
                  running = false;
                  return;
            }

            vec.add(object);      
      }

      public int getSize ()
      {
            return vec.size();
      }

      public Object extract ()
      {
            if (vec.size() == 0)
                  return null;

            Object object = vec.get(0);
            vec.remove(0);
            
            return object;
      }
}

Essentially, if the client were to tell the server to move its piece one unit to the left, it would add a serialized integer to the transmitter.

Now, am I correct in assuming that using ObjectStreams won’t bring about decent network play? Is there something that I’m doing wrong here?

Thanks!

  1. If you must use objects, use RMI. It’s a lot to read up on at first, but once you understand it it’s quite simple. And it’s (almost) perfect for sending objects over the network.

  2. For games, you practically never send objects. Send basic datatypes (e.g. “int”) using raw InputStreams and OutputStreams. Even better, encode everythign to bytes manually before sending (but you don’t need to worry about that for now - for most small games you can get away without).

You can even just send messages as Strings (which use much more space than raw int’s etc) and do Integer.parseInt(…) etc at the receiving end, and you’ll find it’s still 10 times faster than ObjectOutputStream etc… This has the advantage that your messages can be easily made human-readable when you debug :slight_smile:

Now, if only my Structs RFE were implemented…

Cas :slight_smile:

Thanks for the replies. I’ll definitely take a look at RMI. :slight_smile:

Using the ObjectOutputStreams, on a somewhat moderate campus network, the reaction time between the client rotating a piece and actually seeing the result felt like 0.33 - 0.5 seconds. I’m curious to see what it’ll look like on a high-performance, low-latency home LAN.

There are two points I’m curious about, and I’m wondering if anyone knows off hand:

  • Which is faster? Sending five objects, each in succession, or sending all five objects encapsulated within a single array? (5 sends vs. 1 send)

  • Is calling ObjectOutputStream.reset() after writing out the data a good / bad idea?

Always batch up as much stuff as you logically can.

Cas :slight_smile:

Now here’s the thing. How can ObjectOutputStream be so slow? I wrote my object states to the hard drive, and they’re incredibly small!

These are the only objects being passed back and forth:

GameData: 635 bytes, done once every 1 - 10 seconds
KeyStroke: 47 bytes, done 5 - 8 times a second
Position: 560 bytes, matches KeyStroke

The campus network here is certainly more than capable of sustaining 5 kb/s.

The performance hit comes not from the amount of data being sent (in this case), but from the overhead associated with serializing/deserializing the objects. When you do it so frequently, it really adds up.

Alright, here’s an update on the situation. :slight_smile:

Instead of passing various objects back and forth, I switched to encoding Strings into bytes, and passing that along with a BufferedWriter.

The ping times went down from a spikey average of 240 down to 15 milliseconds.

16x Faster :slight_smile:

Thanks!

That’s the way to do it ;D