# TCP multiClientServer mit socket mittels ObjectOutputStream



## alex_Coding (29. Mai 2020)

Hallo zusammen, 

ich Rahmen eines Praktikums im Studium war die Aufgabe Objecte über einen Socket zu verschicken.Dies sollte mitteln eines multiClientServer geschehen. Das Verschicken der Objekte muss zwingend über einen ObjectOutputStream realisiert .Derzeitig werden nur String Objekte verschickt, zum Testen...

Unten befinden sich 3 Klassen: Server, ClientHandler, und der Client. Um leichter einen Einstieg zu finden, habe ich den Code eingefügt.

Ich starte also die Main Methode der Server Klassen und im Anschluss führe ich die Main des Client aus.

An der Stelle 
 System.out.println("bis hier und nicht weiter"); innerhalb des ClientHandler.class ist dann irgendwie Schluss. Dies wird noch ausgegeben.

Aber der Thread wird nicht gestartet, also glaube ich zumindestens, auf jeden Fall wird der print Befehl innerhalb des Construktors von CLientHandler nicht ausgegeben.


Ich habe die Dateien zudem in den Anhang gepackt.

Vielen Dank im voraus,  ich bin überfrage





```
package tcp_final;

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

//Java implementation of  Server side 
//It contains two classes : Server and ClientHandler 
//Save file as Server.java 



//Server class 
public class Server  
{ 
 public static void main(String[] args) throws IOException  
 { 
     // server is listening on port 5056 
   
    ServerSocket ss = new ServerSocket(8080); 
    System.out.println("Server gestartet auf Socket: "+ ss.getLocalPort());  
     // running infinite loop for getting 
     // client request 
     while (true)  
     { 
         Socket s = null; 
         
         try 
         {     
             
             // socket object to receive incoming client requests 
             s = ss.accept(); 
               
             System.out.println("bis hier und nicht weiter");
             ObjectInputStream dis = new ObjectInputStream(s.getInputStream()); 
             ObjectOutputStream dos = new ObjectOutputStream(s.getOutputStream()); 
               
             System.out.println("Assigning new thread for this client"); 
             

             Thread t = new ClientHandler(s, dis, dos); 
             t.start(); 
               
         } 
         catch (Exception e){ 
             s.close(); 
             ss.close();
             e.printStackTrace(); 
         } 
     } 
 } 
}
```




```
package tcp_final;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

//ClientHandler class 
class ClientHandler extends Thread  
{ 

 final ObjectInputStream dis; 
 final ObjectOutputStream dos; 
 final Socket s; 


 // Constructor 
 public ClientHandler(Socket s, ObjectInputStream dis, ObjectOutputStream dos)  
 { 
     System.out.println("Run ClientHandler Constructor");
     this.s = s; 
     this.dis = dis; 
     this.dos = dos; 
 } 

 @Override
 public void run()  
 { 
     System.out.println("Thread Run Methode");
     String received; 
  
     while (true)  
     { 
         try { 
             
                
             received = (String) dis.readObject(); 
             System.out.println("Vom Server gesendet: "+ received);
             
             if(received.equals("Exit")) 
             {  
                 System.out.println("Client " + this.s + " sends exit..."); 
                 System.out.println("Closing this connection."); 
                 this.s.close(); 
                 System.out.println("Connection closed"); 
                 break; 
             } 
                 
             ///send to server
             dos.writeObject("Irgendeine Nachricht");
             
                 
         } catch (IOException e) { 
             e.printStackTrace(); 
         } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
     } 
     try
     { 
         // closing resources 
         this.dis.close(); 
         this.dos.close(); 
           
     }catch(IOException e){ 
         e.printStackTrace(); 
     } 

 } 
}
```




```
package tcp_final;
import java.io.*; 
import java.net.*; 
import java.util.Scanner; 

//Client class 
public class Client  
{ 
 public static void main(String[] args) throws IOException  
 { 
     try
     { 
         Scanner scn = new Scanner(System.in); 
           
         // getting localhost ip 
         InetAddress ip = InetAddress.getByName("localhost"); 
         Socket s = new Socket(ip, 8080); 
   
         ObjectInputStream dis = new ObjectInputStream(s.getInputStream()); 
         ObjectOutputStream dos = new ObjectOutputStream(s.getOutputStream()); 
         
         // the following loop performs the exchange of 
         // information between client and client handler 
         while (true)  
         {    
             System.out.println("Client While"); 
             
             String tosend = "Nachricht an den Server";
             dos.writeObject(tosend);
             
             if(tosend.equals("Exit")) 
             { 
                 s.close(); 
                 break; 
             } 
 
             /// Antwort from Server
             String received =  (String) dis.readObject(); 
             System.out.println(received); 
             
         } 
           
         // closing resources 
         scn.close(); 
         dis.close(); 
         dos.close(); 
     }catch(Exception e){ 
         e.printStackTrace(); 
     } 
 } 
}
```


----------



## LimDul (29. Mai 2020)

Das dürfte die Ursache sein:



> Creates an ObjectInputStream that reads from the specified InputStream. *A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header.*





			ObjectInputStream (Java Platform SE 7 )
		


Das erzeugen eines ObjectInputStreams ist eine Blocking-Operation. Der Server muss mindestens sofort den Serialization Header schicken (wie auch immer man das macht, da bin ich raus)


----------



## LimDul (29. Mai 2020)

Nachtrag: Versuch mal die ObjectOutputStreams zuerst zu erzeugen.

Dadurch das du sowohl auf Client als auch auf Server Seite die InputStreams zuerst erzeugt, warten sowohl Client als auch Server, dass der korrekte Header geschickt wird - was nie passieren wird, da keiner der beiden den ObjectOutputStream erzeugen kann.


----------



## kneitzel (29. Mai 2020)

Eine Idee wäre, dass man sofort nach der Verbindung jeweils ein Objekt schickt. Das kann z.B. ein leerer String sein oder einfach eine Object Instanz.

Mit dem Schreiben des ersten Objekts wird natürlich auch der Header geschrieben und die blocking Operation geht weiter. Das bedeutet, dass er zwar immer noch blockiert, aber halt nur, bis das erste Objekt angekommen ist ...

Wichtig ist aber auch, dass man die Erstellung auf Client / Server in unterschiedlicher Reihenfolge macht. Wenn sowohl Server als auch Client zuerst den InputStream öffnen, warten beide auf ein erster Objekt. Also sollte man da ggf. so hingehen, das immer erst der OutputStream erstellt wird und ein erstes Objekt gesendet wird (um den Header abgearbeitet zu haben).


----------



## kneitzel (29. Mai 2020)

Noch eine kleine Korrektur bezüglich meiner Antwort:
Es scheint so, als ob der Header direkt geschrieben wird und nicht beim ersten Objekt, das gesendet wurde.

Daher ist dieses erste Objekt *nicht* notwendig, sondern es sollte ausreichen, die ObjectOutputStreams zuerst zu erstellen. So deute ich zumindest meinen Test, der einfach nur einen ObjectOutputStream erzeugt und sofort wieder schließt.


----------



## alex_Coding (29. Mai 2020)

hi,
vielen Dank für die schnellen Antworten...

Ich frage mich wie ich denn einfach einen ObjectOutputStream erzeuge, mit ObjectOutputStream oos= new ObjectOutputStream()
Hier kriege ich immer das Problem, dass ich Problem mit dem Konstruktor.

  ObjectInputStream dis = new ObjectInputStream(s.getInputStream());
  ObjectOutputStream dos = new ObjectOutputStream(s.getOutputStream());

  Ich muss ja irgendwie auch den socket beim Erstellen des ObjectOutputStream mitgeben.
 Wie hast du dir das denn gedacht bzw realisiert?

Danke nochmal


----------



## kneitzel (29. Mai 2020)

Einfach die beiden Zeilen sowohl im Client als auch im Server vertauschen:

ObjectInputStream dis = new ObjectInputStream(s.getInputStream());
ObjectOutputStream dos = new ObjectOutputStream(s.getOutputStream()); 
ObjectInputStream dis = new ObjectInputStream(s.getInputStream());


----------



## alex_Coding (29. Mai 2020)

haha krass, it's magic and it works...

Hammer Typ, Hammer Beitrag, Hammer Forum.

Spass bei Seite, vielen vielen Dank 
Vielen Dank


----------



## LimDul (29. Mai 2020)

Das bestärkt mich übrigens in meiner Ansicht, dass Serialisierung und ObjectInput/OutputStreams evil sind. Das ein XXInputStream-Konstruktor, der nur einen anderen InputStream als Eingabe bekommt eine Blocking-Operation ist - das finde ich definitiv nicht intuitiv.  Es ist zwar im JavaDoc dokumentiert, aber ich hab da nur geschaut, weil das anhand der Beschreibung und des Codes die einzige Stelle sein konnte, die blockiert. Aber da wäre ich ohne Dokumentation nie drauf gekommen.


----------



## kneitzel (29. Mai 2020)

Also erst einmal schön, dass jetzt alles klappt.

Und ja, ich gebe Dir voll und ganz Recht @LimDul. Aber das erinnert mich an eine Diskussion mit dem allwissenden Junior Developer oder Wissenschaftler oder was auch immer, der darin DIE Lösung sah. Alles Andere ist Müll 

@alex_Coding Aber unabhängig von der Einschätzung, ist es hier natürlich explizit gefordert und zum Anderen ist das natürlich relativ trivial aufzubauen, so es nur um kurze Übungen geht. 

Und was mir bei diesem Beispiel in den Sinn kommt: Ist die serialVersionUID von String (und anderen Framework Klassen) gleich über Versionen, Anbieter und Betriebssysteme? Also Oracle JDK vs. Zulu vs. AdoptOpenJDK, ... über dieverse Versionen (8,9, ..., 14) und OS (Windows 32/64 Bit, Linux, Mac, AIX, ...)... Muss ich das erst testen oder weiss das jemand durch Zufall?


----------



## fhoffmann (29. Mai 2020)

JustNobody hat gesagt.:


> Ist die serialVersionUID von String (und anderen Framework Klassen) gleich über Versionen, Anbieter und Betriebssysteme?


Zumindestens in Code der Klasse String (von unterschiedlichen Versionen) habe ich folgenden Text gefunden

```
/** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;
```
Wäre ja auch ziemlich blöd, wenn man Strings nicht seialisieren könnte.


----------



## kneitzel (29. Mai 2020)

Danke für die Antwort.

Ich habe es auch eben einmal etwas getestet und auf dem Windows PC (Zulu Java 8, Adopt OpenJDK 11) sowie auf AIX (IBMs Java 8) kam immer genau diese Zahl...

Und ist auch tatsächlich logisch, denn man selbst legt bei sich ja auch die serialVersionUID fest und das würde ja nichts bringen, wenn die eigentlichen Klassen, auf denen man aufbaut, nicht auch feste Versionen hat. Also auch mit etwas Nachdenken war das eigentlich logisch ...


----------



## mihe7 (3. Jun 2020)

alex_Coding hat gesagt.:


> Hammer Typ, Hammer Beitrag, Hammer Forum.


Ich bin für eine Umbenennung: @JustNobody -> @JustTheHammer   Wie ist das hier, kann man das demokratisch beschließen?


----------

