# Server mit meheren Streams/Aufgaben?



## Behnke (30. Jul 2010)

Hallo Zusammen,
ich hab mich ein bisschen in Sockets eingelesen und auch einen kl. Chat gebaut.
Ist ja ne Basis-Aufgabe. So meine Frage ist, ich möchte nun z.b. das man sich Registrieren kann,
das heißt das der Server also einen Befehl bekommt und zum Nutzer erstmal nur ne Text-datei anlegt.

So meine Denkblockade:
Es gibt ja einen Socket.InputStream. Ist der für alles Zuständig? Also darüber läuft ja jetzt quasi der Chat. Wie bring ich da nun eine zweite Aufgabe unter?
Kommt alles an diesem InputStream an oder kann es zwei geben?
Also quasi.
1.)jeweils nur einen Data/Input/Object-Stream?
2.)oder nur einen Stream egal welcher und ich muss quasi da differenzieren was ich machen will?

Hoffe ihr versteht wo meine Blockade ist, muss eine zweite Kommunikationsaufgabe einbauen und hab das noch nirgends in einem Tutorial gelesen. Die Basis mein ich verstanden zu haben.

Gruß Behnke

edit: Ich bräuchte beim Clienten auch ne zweite Schiene der Kommunikation fällt mir grad auf?
Muss ich einfach noch einen Socket erstellen?
*verwirrt*


----------



## Gast2 (30. Jul 2010)

Behnke hat gesagt.:


> Ich bräuchte beim Clienten auch ne zweite Schiene der Kommunikation fällt mir grad auf?
> Muss ich einfach noch einen Socket erstellen?


nein - einfach ein vernüftiges Protokoll für die Kommunikation definieren ... im Moment kann Dein Protokoll nur Nachrichten (also Chat) senden ... das Protokoll wird also komplexer


----------



## Behnke (30. Jul 2010)

Das heißt z.b.: Ich definiere # als Befehlsanfang.
Beispiel: "#Register Nickname" vom Client

Der Server filtert also dann "#.." im InputStream und sieht dann aha Register und ich ruf meine Funktionen auf und Antworte je nachdem wieder zum Clienten.
Ist das korrekt?

Gruß

Edit: hab grad ein alten Thread gefunden!


> Zitat von tuxedo
> 
> -> Nachricht beginn
> 1 byte -> Beschreibt welche Art von Nachricht es ist (0..255... Da kannst du genug Nachrichtentypen definieren)
> ...



Klar macht ja auch mehr Sinn, weil sonst könnte jemand im Chat mit #Register rumspamen und ich leg dafür Benutzer an.
Weitere Tips immer her, ansonsten probier ich mich mal daran!


----------



## ARadauer (30. Jul 2010)

> Es gibt ja einen Socket.InputStream. Ist der für alles Zuständig? Also darüber läuft ja jetzt quasi der Chat. Wie bring ich da nun eine zweite Aufgabe unter?


threads... 

ok, dan kram ich mal in alten beispielen... hier ein alter rechen server den man über browser ansprechen kann..

also für jeden eingehenden request wir ein eigener thread erzeugt... 

```
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.Random;
 
public class MyServer extends Thread {
 
   public static void main(String[] args) throws IOException {
      ServerSocket socket = new ServerSocket(1234);
      while(true){
         System.out.println("waiting...");
         new MyServer(socket.accept()).start();
         
      }
   }
   
   Socket client;
   
   public MyServer(Socket client){
      this.client = client;      
      
   }
   
   public void run(){
      try {
         String request = parseRequest();
                
         
         String msg = "<b>"+handleRequest(request)+"</b><br> proof of alive:"+System.currentTimeMillis();
         sendResponse(msg);
      } catch (IOException e) {
         System.out.println("FEHELER");
         e.printStackTrace();
      }finally{
         try {
            client.close();
         } catch (IOException e) {}
      }    
   }
   /**
    * Protokoll: "cmd?param1:param2"
    * @return
    */
   public String handleRequest(String request){
      
          try {
         System.out.println(request);
         int start = 1;
         int end = request.indexOf("?");
         
         String cmd = request.substring(start, end);
         start = end+1;
         end = request.indexOf(":");
         
         int param1 = Integer.parseInt(request.substring(start, end));
         start = end+1;
         end = request.length();
         int param2 = Integer.parseInt(request.substring(start, end));
         
         if(cmd.equals("add")){
            return ""+(param1+param2);
         }else if(cmd.equals("sub")){
            return ""+(param1-param2);
         }else{
            return "cmd nicht bekannt";
         }
      } catch (Exception e) {
         e.printStackTrace();
         return "ERROR";
      }      
      
      
      
   }
   
   public String parseRequest() throws IOException {
      BufferedReader reader =  new BufferedReader(new InputStreamReader(client.getInputStream()));
         String request = reader.readLine();
         if (request == null || !request.startsWith("GET ") || !(request.endsWith(" HTTP/1.0") || request.endsWith("HTTP/1.1"))) {
             return null;
         }            
       return request.substring(4, request.length() - 9);   
      
      
   }
   
   public void sendResponse(String data) throws IOException{
     
      BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());      
     
         out.write(("HTTP/1.0 " + 200 + " OK\r\n" + 
                    "Date: " + new Date().toString() + "\r\n" +
                    "Server: MyServer/1.0\r\n" +
                    "Content-Type: text/html\r\n" +
                    "Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n" +
                    "Content-Length: " + data.length() + "\r\n" +
                    "Last-modified: " + new Date().toString() + "\r\n" +
                    "\r\n").getBytes());
         out.write(data.getBytes());
         out.close();
     
   }
}
```


----------



## Behnke (30. Jul 2010)

@ARadauer: Threads hab ich ja schon drin.
Mir gings eher um die Stream Angelegenheiten bei dem ich mir nun versuch mit einem Protokoll zu helfen.
Ich mein 2 Threads mit jeweils 1 InputStream?
Obwohl klingt auch garnicht so doof, weil mein Protokoll dann ja abfängt ob es für Chat/Register bestimmt ist.
Aber: kommt das auch 2x an? Sobald sich das ein InputStream holt ist es doch kein zweites mal mehr da oder?


----------



## ice-breaker (30. Jul 2010)

Behnke hat gesagt.:


> Klar macht ja auch mehr Sinn, weil sonst könnte jemand im Chat mit #Register rumspamen und ich leg dafür Benutzer an.
> Weitere Tips immer her, ansonsten probier ich mich mal daran!


ich glaube mein Beispiel eines binären Protokolls ist zu Anfang etwas schwerer 

Du nutzt immer nur *1 InputStream*, außerdem musst du *jede* übertragene Aktion mit dem Protokoll machen, nicht nur Sonderbefehle 
also:

```
#REGISTER meinname
#CHAT hi wie gehts
```

würde nun jemand versuchen 
	
	
	
	





```
#REGISTER maxmustermann
```
 im Chat zu senden kommt da folgendes heraus:

```
#CHAT #REGISTER maxmustermann
```
es wird also also dadurch immer als Chat-Nachricht interpretiert.


Ein gutes Format für das Protokoll zu definieren ist dann die nächste schwere Aufgabe.


----------



## Tomate_Salat (30. Jul 2010)

ice-breaker hat gesagt.:


> Ein gutes Format für das Protokoll zu definieren ist dann die nächste schwere Aufgabe.



Siehe hier. Derzeit bin ich auch gerade dabei ein relativ simples Protokoll zu entwickeln, welches aber schwierigkeiten bei Arrays und listen etc hat.

Derzeit überlege ich ob gson (also json) eine alternative für komplexere Objekte wäre. Was ich schnell gemerkt habe: bleib weg von: Objekte über ObjectOutputStream zu senden, die sind einfach zu Groß.

MFG

Tomate_Salat


----------



## tuxedo (30. Jul 2010)

Tomate_Salat hat gesagt.:


> bleib weg von: Objekte über ObjectOutputStream zu senden, die sind einfach zu Groß.



Wo hast du das denn her? Klar, wenn du statt Chat-Nachricht und Benutzername (zwei Strings, evtl gewrappt in einem "Transportobjekt") den ganzen JFrame sendest, ja, dann hast du Overhead wie blöd.

Aber man man mit OOS/OIS durchaus byte-sparend Dinge übertragen: Einfache Transportobjekte die nach Möglichkeit nur aus primitiven bestehen. 

Mach ich mit SIMON (und RMI machts nicht anders) auch so. Und die Bandbreite fliegt einem keineswegs um die Ohren...

- Alex


----------



## Tomate_Salat (30. Jul 2010)

...Getestet?!

Schau mal in meinen Block. Klar ist das Protokoll nicht ausgereift, aber mit einfachen Datentypen schlage ich den ObjectOutputStream schon jetzt um längen! Aber du kannst dir schon eine Menge bytes einsparen, wenn du z.B. auf JSON setzt.

*Anmerkung:* und mein Update für das Protokoll wird zwar mehr Bytes brauchen, aber trotzdem noch merklich weniger als der OOS und er kann bisher Objekte behandeln, welche variablen vom Typ Number(int, float,...), Character, String und referenzen auf Objekte sind.


----------



## tuxedo (30. Jul 2010)

Klar, wenn du einen Handoptimierten Serialisierungs-Algo benutzt, dann ist das immer sparsamer als wenn man einfach "unbedacht" den OOS benutzt. 

Aber nimm ein flaches Transport-Objekt das nur primitive und Arrays aus solchen beinhaltet und jag das durch den OOS. SO VIEL overhead produziert das nicht. 

Hab Anafangs auch gerätselt ob ich SIMON nicht noch weiter optimieren kann, hier ein zwei byte sparen, dort nochmal eins... Aber braucht man das?

Codesample:


```
public class Test
{
    
    static class MyTransportClass implements Serializable {
        String s = "HelloWorld"; // 10 bytes + 4 bytes länge
        int i = Integer.MAX_VALUE; // 4 bytes
        int ii = Integer.MIN_VALUE; // 4 bytes
        long l = Long.MAX_VALUE; // 8 bytes
        long ll = Long.MIN_VALUE; // 8 bytes
        boolean b = false; // 1 byte
        
        // total: 14+4+4+8+8+1=39 bytes
    }
    
    public static void main (String[] args) throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        
        MyTransportClass mtc = new MyTransportClass();
        oos.writeObject(mtc);
        System.out.println(baos.toByteArray().length); // --> 164 bytes
    }

}
```

39 bytes in einem Objekt zu serialisieren, kostet 164 bytes. Macht einen Overhead von 125 bytes.

Klar, das hört sich viel an. Aber hallo... 125 bytes ... Das flitzt selbst über die GPRS Leitung meines Handy nocht mit drüber ohne dass ich's merke ...

Früher, zu 14k4 Modem zeiten ja... Oder 1k2 modem zeiten. Aber im Zeitalter von DSL und Handy-Flats ist es mir wurscht ob ich ein paar bytes extra habe.

Auf der anderen Seite kann man argumentieren: Klar, wenn ich jetzt 1000 solcher Objekte übertragen muss, dann sind das schon 125.000 bytes. Aber selbst hier kommt der durchschnittliche Netzwerkanschluss ohne weiteres mit zurecht. 

Auf der anderen Seite: Man muss ja keine Transportobjekte nutzen... Man kann auch ein eigenes Marshalling, wie es RMI z.B. macht nutzen... Ist das zu übertragende etwas etwas "primitives", so nutzt man DataOutputStream und schiebt es direkt durch. Da gibts dann gar keinen Overhead. Ist es allerdings ein Objekt, so nutzt man OOS. Fertig.

Aber letzten endes kommt es auf den Anwendungsbereich an. Und somit gibt's keine ultimative Lösung. Jeder wie er es für richtig hält ...

- Alex

P.S. JBOSS Remoting nutzt eine serialisierung die schneller und besser sein soll als die von Java direkt. Sollte man sich vermutlich mal anschauen: JBoss Serialization - JBoss Community


----------

