# Networking to Slow - Java Multiplayer Pong

This topic is 1867 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey all!
I created a Pong Game in Java and added Networking,
but it seems to slow the whole game down signifficantly.

I use a thread for the networking routine, on both server and the two clients.
The conversion is as follows:

send a marker (so the receiver knows which value will be sent next)
send the value
send the next marker, etc...

This is the Code for the Server:

 /** * * Klasse (Thread) welche die Netzwerkfunktionen abhandelt * */ class Network implements Runnable { Network() { } public void run() { // mit Host verbinden und Kommunizieren try { ServerSocket server = new ServerSocket(8888); System.out.println("Warte auf Verbindung auf Port 8888"); Socket Client1 = server.accept(); System.out.println("Server: Client1 verbunden"); Socket Client2 = server.accept(); System.out.println("Server: Client2 verbunden"); //Client1 einrichten OutputStream out1 = Client1.getOutputStream(); PrintStream ps1 = new PrintStream(out1, true); // Second param: auto-flush on write = true InputStream in1 = Client1.getInputStream(); BufferedReader br1 = new BufferedReader(new InputStreamReader(in1)); //Client2 einrichten OutputStream out2 = Client2.getOutputStream(); PrintStream ps2 = new PrintStream(out2, true); // Second param: auto-flush on write = true InputStream in2 = Client2.getInputStream(); BufferedReader br2 = new BufferedReader(new InputStreamReader(in2)); String line=""; ps1.println("run"); ps2.println("run"); while(true){ //Einlesen Client1 for(int i=0;i<1;i++){ line = br1.readLine(); if(line!=null){ if(line.contains("v1")){ line = br1.readLine(); player1Y=Integer.parseInt(line); } } }//end Einlesen for-Schleife //Einlesen Client2 for(int i=0;i<1;i++){ line = br2.readLine(); if(line!=null){ if(line.contains("v1")){ line = br2.readLine(); player2Y=Integer.parseInt(line); } } }//end Einlesen for-Schleife //Ausgabe an Client1 ps1.println("v2"); ps1.println(""+player2Y); ps1.println("v4"); ps1.println(""+ballY); ps1.println("v3"); ps1.println(""+ballX); ps1.println("v5"); ps1.println(""+player1Score); ps1.println("v6"); ps1.println(""+player2Score); //Ausgabe an Client2 ps2.println("v2"); //gespiegelt für Player 2 ps2.println(""+player1Y); ps2.println("v4"); ps2.println(""+ballY); ps2.println("v3"); ball2X=MAXX-ballX; //gespiegelt für Player2 ps2.println(""+ball2X); ps2.println("v5"); //gespiegelt für Player2 ps2.println(""+player2Score); ps2.println("v6"); //gespiegelt für Player2 ps2.println(""+player1Score); } } catch (IOException e) { // TODO Auto-generated catch block //e.printStackTrace(); System.out.println("Verbindungsfehler aufgetreten"); } } } 

And the Client:

 /** * * Klasse (Thread) welche die Netzwerkfunktionen abhandelt * */ class Network implements Runnable { String hostAdress; Network(String adress) { this.hostAdress = adress; } public void run() { try { Socket connectionToTheServer = new Socket(hostAdress, 8888); System.out.println("Client: Verbindung zu Server hergestellt"); InputStream in = connectionToTheServer.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); OutputStream out = connectionToTheServer.getOutputStream(); PrintStream ps = new PrintStream(out, true); String line=""; while(!(line.equals("run"))){line=br.readLine();}//warten auf Start des Spiels while(true){ //Ausgeben ps.println("v1"); ps.println(""+player1Y); //Einlesen for(int i=0;i<5;i++){ line = br.readLine(); if(line!=null){ if(line.contains("v2")){ line = br.readLine(); player2Y=Integer.parseInt(line); }else if(line.equals("v3")){ line = br.readLine(); ballX=Double.parseDouble(line); }else if(line.equals("v4")){ line = br.readLine(); ballY=Double.parseDouble(line); }else if(line.contains("v5")){ line = br.readLine(); player1Score=Integer.parseInt(line); }else if(line.contains("v6")){ line = br.readLine(); player2Score=Integer.parseInt(line); } } }//end Einlesen for-Schleife } } catch (UnknownHostException e) { // TODO Auto-generated catch block //e.printStackTrace(); System.out.println("Verbindungsfehler aufgetreten"); } catch (IOException e) { // TODO Auto-generated catch block //e.printStackTrace(); System.out.println("Verbindungsfehler aufgetreten"); } } 

Logically everything works fine, but its very slow.

I would appreciate some recommendations on how to improve the speed!

##### Share on other sites
Have you tried setTcpNoDelay?

##### Share on other sites

Have you tried setTcpNoDelay?

Thank you so much!
I added it and its much better now
Is there anything else i can do?

##### Share on other sites

[Thank you so much!
I added it and its much better now
Is there anything else i can do?

- UDP
- Better network/packet structure

##### Share on other sites
For the issue at hand the choice between TCP and UDP are irrelevant, especially in any local test.

##### Share on other sites

- Better network/packet structure

You mean not to send single lines with auto-flush?

##### Share on other sites
ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

nodelay is good in high-speed games (disabling nagle algo), but it might not always be enabled..
the drivers are free to do nothing, after all, and never tell you otherwise
udp isn't really any faster, it's just potentially smaller packets, and widely used for "positioning" that are high-frequent updates
when working with networking, you will never have a cpu bottleneck compared to high latency
high latency is measured in hundreds of milliseconds, and there isn't much you can do about it
increasing the amount of updates you are doing by too much will just cause the socket to throttle (and die) because its write buffer would be exceeded
the normal latency for local loopback is 0ms, LAN is 1ms+ and internet is at least 60ms
60ms is alot considering 30fps is 33ms, 60ms is ~16fps
that's why you'll always _always_ need to interpolate between moving visuals with high-frequency updates
Edited by Kaptein

##### Share on other sites

ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

So i should let the Clients kinda estimate where the ball will be on the next update?
Or insert an interpolated point between two of the updates?

##### Share on other sites

[quote name='Kaptein' timestamp='1355399101' post='5010161']
ideally, your code should handle 250-500ms lag, and thus you'll need to interpolate between the data sent to you from another player
this is a game where lag will affect gameplay, and we don't care about cheating, so, i would just do very simple interpolation in between updates
and not worry so much about the actual amount of latency or updates..
the biggest problem you will face is "invariance," here being desynchs, but since you have a centralized server that makes all the decisions
this problem is already solved =)

So i should let the Clients kinda estimate where the ball will be on the next update?
Or insert an interpolated point between two of the updates?
[/quote]

yep, you have to. it's not a decision or design choice.. i updated my post and added some context as to why we have to do this and make do
i used 60ms in my example, but i've never had such a low latency on my connection it's usually in the 90's (97ms for example)
what this means is usually only that the response from me to the server will be delayed by 97ms, and that the movement will STILL be fluid
but ANY point in between me and the server could delay data unreliably before sending it
which means that it won't always be fluid, especially if the nodes wait too long to send and just gather your incoming data,
then send it in one bigger package (causing the server to get 10-20 updates in a single read())

TCP has delayed ACK (which can be delayed a long time, even 200ms) and nagle algorithm isn't reliably disabled (and you shouldn't want to disable it either)
in short: use UDP for your game if you want the fastest throughput of small updates
but, personally i wouldn't go that far Edited by Kaptein

##### Share on other sites
You could also send/receive a byte array instead of using character stream as you seems to be only sending Numbers. I know it prolly won't improve a lot the speed but
optimization is always good. Go read about java object serialization/deserialization. You will save time by not creating a String and parsing it as you just want to send numbers. Edited by PsychotikRabbit

##### Share on other sites
Ok, I will try to implement that.

PsychoticRabbit: But it will only affect the CPU time, not network itself that much i guess?

##### Share on other sites
I didn't test it but I'm pretty sure it would improve the network time. You won't need to open an output stream on the socket to write a string and to open an input stream on the socket to read a string. It would improve both CPU and network time. You could write a small unit test to test out the time sending and receiving a string by streams and another test for the time sending deserialized object and receiving the byte array.

##### Share on other sites
any packets that are smaller sized than the maximum packet size won't have an effect on throughput or latency at all
think of packetsize in GPU terms, if you draw very little with the gpu, the gpu will spend a measurable time "working on working with" the data
all packets that are <= the max size of the what the links on the route allows, will pass through intact
using as big as possible packetsize, but <= the correct packet size gives you the best throughput
for windows a MTU (frame) defaults to 1500 bytes (i think) over broadband, but that does not affect TCP packet size!
i imagine that tcp packetsize is at least 8kb, perhaps 16kb by now
i don't think you will be sending anything close to 8kb of data in one go, and it will most likely be alot less than the MTU size
in short: wether or not you use string stream, bit stream or serialized objects, it doesn't matter
java is a long-standing framework for JVM that most likely sends any kind of data just as effective as another

what really matters is how fast you try to send the tcp packets, and that they don't get split up (doubling the queue)
edit: and don't disable nagle
if you need the data to pass through immediately, use UDP, but it's not a recommended protocol
many firewalls block it.. among other things, UDP doesn't guarantee the order of packages arrive in the same order it was sent
Edited by Kaptein