# Was für ein Stream soll ich verwenden?



## Wagner (13. Jan 2008)

Hallo zusammen,

Ich bin grad dabei ein Spiel netzwerkfähig zu machen, aber ich weiß nicht genau (auf Grund von Erfahrungdefizit) wie ich das Problem angehen soll. Das Netzwerk soll ja schließlich schnell sein, aber dennoch recht simpel.

Ich hab vor kurzem ein Server-Client-Programm geschrieben, das Objekte hin und her schicken kann, weil sich das für mich am logischsten angehört hat. Jetzt hab ich aber mal per Localhost geschaut, wie lang das braucht.

Bin dabei auf rund 250ms gekommen und das scheint mir doch sehr lang.

Jetzt meine Frage: Ist es sinnvoller ein ByteStream oder ähnliches zu verwenden? Wenn ja, wie wandle ich dann z.B. Floats in ein Byte um?

Vielen Dank für eure Antworten im Vorraus

mit freundlichen Grüßen
Patrick


----------



## Guest (13. Jan 2008)

Du kannst DataInputStream/DataOutputStream verwenden. Es hat Methoden zu Lesen und Schreiben von Float und anderer Typen.


----------



## tuxedo (14. Jan 2008)

Die verbleibenden 250ms liegen am Nagle-Algorithmus (siehe auch hier im letzten Post). Den kannst du aber abschalten:


```
socket.setTcpNoDelay(true);
```

Am Stream-Typ selbst liegt das nicht wirklich. 

- Alex


----------



## Wagner. (14. Jan 2008)

Ich erreich keine Verbesserung dadurch : /
Was mach ich falsch? 

Der Client schickt ein Objekt an den Server.
Der Server hat mehrere Objekte, und sobald der Server das Objekt vom Client bekommt, schickt er alle Objekte an den Client (dieser speichert dann alle Objekte in einem Objekt[])

Ist dieser Algorithmus angepasst für ein Spiel? Oder muss ich mehr auf meine Ressourcen (bzw. Übertragungsgeschwindigkeit) achten?


----------



## tuxedo (15. Jan 2008)

wenn du nicht gerade Kilobyte-weise Daten verschickst, solltest du, wenns dir auf die Geschwindigkeit ankommt, direkt nach dem Senden des Objektes ein .flush() auf deinen Ausgabestrom machen. 

AFAIK ist der Standardpuffer 8kbyte groß. D.h. wenn du nur 1 oder 2 kbyte sendest, gehen die nicht "sofort" auf die Reise, sondern erst wenn der Puffer von 8kbyte voll ist. Das könnte deine noch übrigen "verzögerung" sein.

Wenn nicht:

Lass mal sehen "wie" du versendest und empfängst und versuch mal ein Minimalbeispiel zu basteln. 

- Alex


----------



## Wagner (19. Jan 2008)

Okay,

ich versuch mal kurz zu erklären.

Also, folgendes Prinzip erstmal:

- Server legt ein Array von Objekt an.
- Client schickt ein Objekt mit einer bestimmten ID.
- Server speichert anhand der ID das Objekt an einer bestimmten Stelle.
- Danach "durchläuft" der Server das Array und zählt die vorhandenen Objekte.
- Diese Anzahl gibt der Server als erstes wieder an den Client zurück, damit der Client weiß, wie oft er auf ein Input warten muss.
- Danach durchläuft der Server das Array und sucht nach Stellen, die ungleich "null" sind und tut diese in den Output.
- Nachdem er alle im Output hat, kommt der Befehl "out.flush()".
- Dann beginnt der Client diese Objekte zu verarbeiten (dieser hat vorher ebenfalls ein Array angelegt), er bekommt auch die Objekte mit den entsprechenden ID's und speichert diese dann wiederrum in ein Array (damit er bei unserem Spiel alle Gegner bei sich anzeigen kann).

Hier der Code:

Das ist das Objekt (ist bis jetzt nur ein Dummie)

```
import java.io.Serializable;
import javax.vecmath.Point3d;

public class Objekt implements Serializable{
    
    public int ID;
    public int Anzahl_Objekte;    //Damit wird nachher die Anzahl an den Client gegeben
    public Point3d p0 = new Point3d();

    public Objekt() {

    }
    
}
```

So, nun zum Client:

```
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;

public class Client {
    
    Objekt[] alleObjekte = new Objekt[10]; //die hat nichts bestimmtes zu bedeuten!
    
    public Client()  throws IOException, ClassNotFoundException {

        long x = System.currentTimeMillis();
        
        Socket server = new Socket("localhost", 4242 );
        server.setTcpNoDelay(true);
        ObjectOutputStream out = new ObjectOutputStream(server.getOutputStream());
        ObjectInputStream in = new ObjectInputStream(server.getInputStream());


 
       
        Objekt bla = new Objekt();
        bla.ID = 2; //variiert...kommt auf den Client an!
        out.writeObject(bla);
        out.flush();

//Bis hier her sollte eiglt alles klar sein
        
    
/*************************
* - Der erste Input ist die Anzahl der Objekt
* - So wurde es mit beigebracht zu casten : / (ich weiß nicht, ob das elegant ist)
**************************/
    
        Object test1 = in.readObject();
        Objekt test2 = null;
        
        if (test1 instanceof Objekt) {
            test2 = (Objekt)test1;            
        }

//Nun hat test2 die Anzahl der Objekte unter der Variablen "anzahl_Objekte" gespeichert

  
/************************************
* - Nun wiederholt der Client anhand der Anzahl das Empfangen, und speichert dies dann
* in der entsprechenden Stelle!
************************************/
      
        for(int i = 0; i < test2.anzahl_Objekte; i++){
            Object o = in.readObject();
            Objekt mo = null;
            
            if (o instanceof Objekt) {
                mo = (Objekt)o;
                alleObjekte[mo.ID] = mo;
                
            }
        }

// P.S. ich weiß, meine Variablennamen sind nicht sehr hilfreich : /
        

 // Hier mach ich einfach einen Test, indem ich überprüf, welche ID's alles gespeichert wurden!
       
        System.out.println("ID's im Array:");
        for(int i = 1; i < alleObjekte.length; i++){
            if(alleObjekte[i] != null){
                System.out.println(alleObjekte[i].ID);
            }
        }
        System.out.println("---Ende---");

        in.close();
        out.close();
        server.close();
        
        long y = System.currentTimeMillis();
        System.out.println("Benoetigte Zeit: "+(y-x));
        
    }
    
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        
        try {
            Client newClient = new Client();
        }catch (IOException ex) {
            ex.printStackTrace();
        }catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }
}
```


Server:



```
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    
    public Objekt[] alleObjekte = new Objekt[25];
    
    public Server() throws IOException, ClassNotFoundException{
        ServerSocket server = new ServerSocket(4242);


        while (true) {
            
            Socket client = server.accept();
            ObjectInputStream in = new ObjectInputStream(client.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
  
 //Wenn der Server etwas von einem Client bekommt, dann speichert er den neuen Wert in der entsprechenden //Stelle!
         
            Object o = in.readObject();
            Objekt mo = null;
            
            if (o instanceof Objekt) {
                mo = (Objekt)o;
                alleObjekte[mo.ID] = mo;
                System.out.println("Folgende ID wurde gespeichert: "+mo.ID);                
            }
            
/******************Anzahl der Objekte**********/
            int anzahl = 0;
            for(int j = 0; j < alleObjekte.length; j++){
                if(alleObjekte[j] != null){
                    anzahl++;
                }
            }
            Objekt test = new Objekt();
            test.anzahl_Objekte = anzahl;
            out.writeObject(test);

//ich weiß, ist nict gerade schön : /
/********************Ende********************/


//Nun durchläuft der Server das Array und wenn an der Stelle ein Objekt ist, schreibt er es in den Output
            
            System.out.println("Folgende ID's wurden geschickt:");
            for(int i = 0; i < alleObjekte.length; i ++){                
                if(alleObjekte[i] != null){
                    out.writeObject(alleObjekte[i]);
                    System.out.println(alleObjekte[i].ID);
                    
                }
            }

//Nachdem er durch ist, kommt der Befel "out.flush();"

            out.flush();
            System.out.println("---Ende---");
            
            out.close();
            in.close();
            client.close();
        }
    }
    
    
    
    public static void main(String[] args) {
        
        try {
            Server newServer = new Server();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        
        
    }
    
    
}
```

*Anmerkung zu alex0801:* Das Objekt zeigt mir im Windows eine Größe von "1KB" an.

Und was mein Problem ist, von dem Zeitpunkt, wo ich den Input und Output im Client erstell bis zum Zeitpunkt, bei dem der Client fertig mit speichern ist, vergeht (mit localhost) zu viel Zeit um damit ein Netzwerkspiel zu machen (*250-600ms pro Durchlauf*)

Und wenn ich vorher schon in dem Array vom Server irgendwelche Objekte angelegt hab (z.b. 6 weitere Objekte) und die dann durch geb, vergeht fast genau so viel Zeit. 

Wie muss ich das verstehen? Wie kann ich dem aus dem Weg gehen? Was sollte ich verbessern (mal abgesehen von der Darstellung und Eleganz)

Schon mal vielen Dank im Vorraus 

mit freundlichen Grüßen
Wagner


----------



## Wagner (26. Jan 2008)

Hat keiner eine Idee, wie ich das Problem best möglich lösen kann? 
Oder zumindest einen besseren Ansatz?

mfg
Wagner


----------



## SebiB90 (26. Jan 2008)

ich hab eher wenig ahnung von netzwerkprogrammierung
aber wäre es nicht logischer setTypNoDelay auf dem Server auszuführen? Weil der verschickt die Daten ja, also muss da doch dieser Algorithmus deaktiviert werden. oder nicht?


----------



## tuxedo (28. Jan 2008)

Das mit dem TcpNoDelay solltest du in der Tat auf beiden Seiten machen. 

Bei der Sache mit dem Casten ... Da du ja exakt weißt, was für ein Typ kommt, musst du keine instanceOf Abfrage machen. Das kostet nur unnötig zeit. Caste direkt in den Typ den du bekommen willst.

Weiterhin ist es nicht ganz so performant wenn du immer objekte schickst. Wenn du eine Zahl/Anzahl von irgendwas übermitteln willst, benutze eine DataInput/OutoutStream. Der kennt writeInt() und readInt(). Das ist etwas schneller. 

>> Das Objekt zeigt mir im Windows eine Größe von "1KB" an.

Wie um Himmels willen hast du das denn ermittelt?

Nebenbei würde ich dir auch fast raten mal RMI oder was ähnliches (SIMON??) anzuschauen... Da sparst du dir die Arbeit mit dem Protokoll und kannst Nachrichten gleichj nen Zacken schneller verschicken ohne viel Aufand zu betreiben. 

Schau dir mal diese Grafik an:





Vielleicht reicht dir diese Geschwindigkeit ja aus.... 

- Alex


----------

