Jump to content

  • Log In with Google      Sign In   
  • Create Account

Networking to Slow - Java Multiplayer Pong


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 miicchhii   Members   -  Reputation: 212

Like
0Likes
Like

Posted 13 December 2012 - 03:07 AM

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!

Thanks in advance!

Sponsor:

#2 BitMaster   Crossbones+   -  Reputation: 4074

Like
1Likes
Like

Posted 13 December 2012 - 03:15 AM

Have you tried setTcpNoDelay?

#3 miicchhii   Members   -  Reputation: 212

Like
0Likes
Like

Posted 13 December 2012 - 03:33 AM

Have you tried setTcpNoDelay?

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

#4 alexc95   Members   -  Reputation: 116

Like
0Likes
Like

Posted 13 December 2012 - 03:52 AM

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


- UDP
- Better network/packet structure

#5 BitMaster   Crossbones+   -  Reputation: 4074

Like
1Likes
Like

Posted 13 December 2012 - 04:15 AM

For the issue at hand the choice between TCP and UDP are irrelevant, especially in any local test.

#6 miicchhii   Members   -  Reputation: 212

Like
0Likes
Like

Posted 13 December 2012 - 04:36 AM

- Better network/packet structure

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

#7 Kaptein   Prime Members   -  Reputation: 2146

Like
1Likes
Like

Posted 13 December 2012 - 05:45 AM

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 =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;

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, 13 December 2012 - 05:51 AM.


#8 miicchhii   Members   -  Reputation: 212

Like
0Likes
Like

Posted 13 December 2012 - 05:50 AM

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 =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;


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?

#9 Kaptein   Prime Members   -  Reputation: 2146

Like
0Likes
Like

Posted 13 December 2012 - 06:02 AM


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 =)

i would implement fast interpolation pad_y = (0.9 * pad_y + 0.1 * pad_last_y); pad_last_y = pad_y;


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?


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())

Note: i didn't speak about advantages of UDP, such as congestion controls, but i'm sure you can read about it on your own
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, 13 December 2012 - 06:04 AM.


#10 PsychotikRabbit   Members   -  Reputation: 168

Like
0Likes
Like

Posted 13 December 2012 - 07:05 AM

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, 13 December 2012 - 07:05 AM.


#11 miicchhii   Members   -  Reputation: 212

Like
0Likes
Like

Posted 13 December 2012 - 07:08 AM

Ok, I will try to implement that. :)
Kaptein, Thank you for your advices and all the informations!

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

#12 PsychotikRabbit   Members   -  Reputation: 168

Like
0Likes
Like

Posted 13 December 2012 - 07:43 AM

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.

#13 Kaptein   Prime Members   -  Reputation: 2146

Like
0Likes
Like

Posted 13 December 2012 - 09:12 AM

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, 13 December 2012 - 09:15 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS