# Objekte versenden, Client-Server



## Einstein97 (20. Jun 2016)

Hallo liebe Community,
ich versuche zur Zeit eine Client-Server-Anwendung zu schreiben, bei der ich Client und Server durch ObjectStreams miteinander verbinde. Anschließend würde ich dann gerne ein String Objekt testweise zum Server schicken und dort ausgeben lassen.
Leider kommt dort aber nichts an...die Verbindung scheint zwar zu bestehen und ich erhalte auch keinerlei Exceptions, aber trotzdem erhalte ich meinen String auf dem Server nicht...
Ich versuche nun mal, möglichst nur den relevanten Code zu zitieren, vielleicht fällt jemandem ja sofort ein Fehler auf.
Liebe Grüße

Hier erzeuge ich einen Client aus meiner Client-Klasse, welcher sich mit dem Server verbindet.
Dann erstelle ich ein Objekt meiner MessageObject-Klasse, welches ich dann mithilfe der Methode sendName() versenden will.

```
public class ClientMain {
 
    public static void main(String[] args) {
       
        Client user= new Client("localhost",5553);
        MessageObject userName= new MessageObject("Test");
        user.sendName(userName);
       
    }
}
```
 
Im Server möchte ich dann mit der Methode getName() den String erhalten.

```
public class ServerMain {
 
    public static void main(String[] args) {
       
        Server server= new Server(5553);
        server.getName();
    
    }
 
}
```
 
Die Objekte dieser Klasse möchte ich versenden, daher implementiere ich das MarkerInterface Serializable.

```
public class MessageObject implements Serializable {
   
    private static final long serialVersionUID=1;
 
    String userName;
   
    public MessageObject(String name){
        this.userName= name;
    }
   
}
```
 

```
public class Client {
   
    Socket server;
    ObjectOutputStream out= null;
    ObjectInputStream in= null;
    MessageObject ob;
   
    public Client(String host, int port){
        try {
            server= new Socket(host, port);
            out = new ObjectOutputStream(server.getOutputStream());
            in = new ObjectInputStream(server.getInputStream());
        } catch (UnknownHostException e) {
              e.printStackTrace();
        } catch (IOException e) {
              e.printStackTrace();
        }
    }
   
    public void sendName(MessageObject o){
        try {
            out.writeObject(o);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
}
```
 

```
public class Server {
   
    MessageObject ob;
    ServerSocket server;
    Socket player;
    ObjectOutputStream out= null;
    ObjectInputStream in= null;
   
    public Server(int port){
       
          try {
            server= new ServerSocket(port);
            System.out.println("Server started...!");
            while(true){
                  player=server.accept();
                  System.out.println("New Player connected: "+ player.getInetAddress().toString());
               
                  out= new ObjectOutputStream(player.getOutputStream());
                  in= new ObjectInputStream(player.getInputStream());
               
            }
          } catch (IOException e1) {
              e1.printStackTrace();
          } finally{
              try {
                player.close();
                out.close();
                in.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
    }
   
    public void getName(){
        try {
          ob = (MessageObject) in.readObject();
          System.out.println(ob);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
}
```


----------



## Jardcore (20. Jun 2016)

Du solltest innerhalb der Schleife prüfen ob etwas angekommen ist.
Dein server.getName() wird aktuell nur einmal nach dem Start des Servers aufgerufen.

Also getName() in die Schleife ziehen und in getName() noch eine Null-Prüfung vor dem cast hinzufügen. Für die Ausgabe des Names musst du dann natürlich noch getName() des MessageObjects aufrufen.

while(true) Schleifen sollte man übrigens vermeiden und getName ist auch ein komischer Name^^ vllt
checkRequest() 

Beste Grüße,
Jar


----------



## Einstein97 (20. Jun 2016)

Hallo Jar, danke für die Antwort.
Vermutlich habe ich da etwas falsch verstanden, es funktioniert leider noch nicht...
Ich soll also in der Klasse Server im Konstruktor das getName in der Schleife verwenden?
Also dann this.getName() ?

Und dann die Nullprüfung in der Methode:

```
public void getName(){
        try {
          if(in.readObject() != null){
            ob= (MessageObject) in.readObject();
            System.out.println(ob);
          }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
```
 
Viele Grüße
Einstein97


----------



## mrBrown (20. Jun 2016)

#getName wird bisher nie ausgeführt, weil im Konstruktor die Endlosschleife gestartet wird (was nicht gut ist - im Konstruktor nur das Objekt erstellen, keinen Logik starten). Sinnvoller wäre es, die Schleife in eine extra Methode auszulangen, und die in der #main aufzurufen. Und in der Schleife wie schon gesagt #getName aufrufen


----------



## Einstein97 (22. Jun 2016)

Guten Morgen,
ich habe das Programm nun nochmals umstrukturiert und beispielsweise auch die Endlosschleife aus dem Konstruktor genommen.
Dennoch funktioniert die Übertragung des Objektes leider nicht...es kommt nichts an beim Server. Hier nochmal der relevante Code, wie er aktuell aussieht:
In den jeweiligen Main-Methoden habe ich einen Client und einen Server erzeugt, ich denke, das ist nachvollziehbar und muss nicht gepostet werden. Vom Client aus erzeuge ich dann eine Message vom Typ String und schicke sie mit der sendMes()-Methode los.
Viele Grüße
Einstein97


```
public Client(String host, int portNumber){
        try {
            server= new Socket(host, portNumber);
            initializeConnection(server);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
 
public void initializeConnection(Socket server){
        try {
            out= new ObjectOutputStream(server.getOutputStream());
            in= new ObjectInputStream(server.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
}
 
public void sendMes(Message mes){
        try {
            out.writeObject(mes);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
```
 


```
public Server(int portNumber, int queueLength){
        try {
            server= new ServerSocket(portNumber, queueLength);
            System.out.println("Server started...!");
            connect(server);
        } catch (IOException e) {
            e.printStackTrace();
        }
}
   
public void connect(ServerSocket server){
        while(true){
            try {
                player= server.accept();
                System.out.println("Connection with new player: "+player.getInetAddress().toString());
                handleConnection(player);
            } catch (IOException e) {
                  e.printStackTrace();
            }
        }
}
   
public void handleConnection(Socket player){
        try {
            out= new ObjectOutputStream(player.getOutputStream());
            in= new ObjectInputStream(player.getInputStream());
            receive();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
   
public void receive(){
        Message ob= null;
        try {
          if (in.readObject()!= null){
            ob= (Message) in.readObject();
            System.out.println(ob);
          }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
```


----------



## Jardcore (22. Jun 2016)

Streng genommen ist die Endlosschleife immer noch im Konstruktor 
Bin gerade auf Arbeit, deswegen hier ein Link in dem die Kommunikation zwischen Server und Client wunderbar erläutert wird.
http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html


----------



## Einstein97 (22. Jun 2016)

Hallo Jardcore,
in diesem Tutorial wird aber gar nicht mit eigenen Konstruktoren gearbeitet. Von daher weiß ich nicht, wie ich das nun auf mein Programm beziehen soll... Komme einfach nicht weiter.


----------



## mrBrown (22. Jun 2016)

Wo wird denn überhaupt die Message geschickt?

Zum Problem mit der Endlosschleife, #connect braucht keinen übergebenen ServerSocket, den hast du schon als Instanzvariable. Du kannst also einfach den Aufruf von #connect aus dem Konstruktor nehmen, und es stattdessen aus der #main aufrufen.


----------



## Einstein97 (22. Jun 2016)

Hier wird in der ClientMain die Message gesendet:

```
public class ClientMain {
 
    public static void main(String[] args) {
       
        Client player= new Client("localhost",6745);
        Message mes= new Message("Test");
        player.sendMes(mes);
   
    }
 
}
```
 
Und hier erzeuge ich einen Server in der ServerMain:

```
public class ServerMain {
 
    public static void main(String[] args) {
       
        int portNumber= 6745;
        int queueLength= 100;
       
        Server server= new Server(portNumber, queueLength);
        server.connect();
    }
}
```
 
Jetzt, wo ich connect() in der main aufrufe, erhalte ich eine SocketException: Connection reset


----------



## mrBrown (22. Jun 2016)

Einstein97 hat gesagt.:


> Jetzt, wo ich connect() in der main aufrufe, erhalte ich eine SocketException: Connection reset


Und an welcher Stelle?


----------



## Einstein97 (22. Jun 2016)

In der Server-Klasse beim Aufruf von handleConnection(player) in Zeile 30,  receive() in Zeile 42 und in der receive-Methode bei ob= (Message) in.readObject(); in Zeile 52.

```
java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.io.ObjectInputStream$PeekInputStream.peek(Unknown Source)
    at java.io.ObjectInputStream$BlockDataInputStream.peek(Unknown Source)
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at Server.receive(Server.java:52)
    at Server.handleConnection(Server.java:42)
    at Server.connect(Server.java:30)
    at ServerMain.main(ServerMain.java:10)
```


----------



## Einstein97 (22. Jun 2016)

Könnte es sein, dass ich die Streams und Sockets noch schließen muss?
In einem finally-Block?


----------



## mrBrown (22. Jun 2016)

Wann startest du Server und wann Client?


----------



## Einstein97 (22. Jun 2016)

Ich starte zunächst die ServerMain und anschließend dann ClientMain.


----------



## mrBrown (22. Jun 2016)

Ich kanns grad nicht testen, aber es könnt daran liegen, das direkt nach dem Senden der Client beendet wird, damit wird auch die Verbindung geschlossen, und der Server kann auf die Verbindung nicht mehr zugreifen.

Lösen könnte man das, wenn man im Client nach dem Senden auf eine Antwort wartet


----------



## Einstein97 (22. Jun 2016)

Also ich habe jetzt auch noch die receive-Methode in der main() aufgerufen, dann läuft das Programm wieder ohne eine Exception zu werfen.

```
public class ServerMain {
 
    public static void main(String[] args) {
       
        int portNumber= 6748;
        int queueLength= 100;
       
        Server server= new Server(portNumber, queueLength);
        server.connect();
        server.receive();
    }
}
```
 
Im Server bleibt dann folgendes:

```
public Server(int portNumber, int queueLength){
        try {
            server= new ServerSocket(portNumber, queueLength);
            System.out.println("Server started...!");
        } catch (IOException e) {
            e.printStackTrace();
        }
}
   
public void connect(){
        while(true){
            try {
                player= server.accept();
                System.out.println("Connection with new player: "+player.getInetAddress().toString());
                out= new ObjectOutputStream(player.getOutputStream());
                in= new ObjectInputStream(player.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
}
 
public void receive(){
        Message ob= null;
            while(true){
                try {
                    ob= (Message) in.readObject();
                    System.out.println(ob);
                } catch (ClassNotFoundException e) {
                      e.printStackTrace();
                } catch (IOException e) {
                      e.printStackTrace();
                }
            }
       
    }
}
```
 
Nur bleibt es dabei, dass die Nachricht nicht zum Server geschickt wird. Zum Verzweifeln.


----------



## Jardcore (22. Jun 2016)

mrBrown hat gesagt.:


> Lösen könnte man das, wenn man im Client nach dem Senden auf eine Antwort wartet


So wie in dem Link beschrieben, den ich geschickt habe


----------



## mrBrown (22. Jun 2016)

Einstein97 hat gesagt.:


> Also ich habe jetzt auch noch die receive-Methode in der main() aufgerufen, dann läuft das Programm wieder ohne eine Exception zu werfen.


Der Client braucht eine recieve-Methode, der Server läuft ehh in Endlosschleife.


----------



## Einstein97 (22. Jun 2016)

Mein Client hat auch eine receive-Methode. Diese sieht genauso aus wie die im Server.
Habe Sie bisher nur nicht hier zitiert, da ich sie für das bloße Senden vom Client zum Server als unwichtig erachtet hatte.
D.h., ich muss jetzt diese receive-Methode im Client auch noch "anwenden", wenn ich etwas zum Server schicke? Den Text im angegeben Link finde ich nämlich etwas irreführend, da die einzelnen Codeabschnitte teilweise doppelt vorkommen, dann nochmals zerlegt werden und man nicht immer weiß, worauf sich der Text dann bezieht


----------



## mrBrown (22. Jun 2016)

Einstein97 hat gesagt.:


> Mein Client hat auch eine receive-Methode. Diese sieht genauso aus wie die im Server.
> Habe Sie bisher nur nicht hier zitiert, da ich sie für das bloße Senden vom Client zum Server als unwichtig erachtet hatte.
> D.h., ich muss jetzt diese receive-Methode im Client auch noch "anwenden", wenn ich etwas zum Server schicke? Den Text im angegeben Link finde ich nämlich etwas irreführend, da die einzelnen Codeabschnitte teilweise doppelt vorkommen, dann nochmals zerlegt werden und man nicht immer weiß, worauf sich der Text dann bezieht


Ja, direkt nach dem Senden muss du auf Antwort warten.


----------



## Einstein97 (22. Jun 2016)

```
public void sendMes(Message mes){
        try {
            out.writeObject(mes);
            out.flush();
            Message fromServer;
            try {
                while((fromServer= (Message) in.readObject())!= null){
                    System.out.println("Server: "+fromServer);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
}
```
 
Habe nun also die sendMes()-Methode des Clients noch um den Teil aus dem Link erweitert.
Verstehe einfach nicht, was das verändern sollte. Bisher habe ich wirklich noch keine einfache Client-Server Anwendung gesehen, in der der Client in dieser Weise auf eine Antwort wartet. Man will doch wirklich nur etwas zum Server schicken.


----------



## mrBrown (22. Jun 2016)

Wenn dein Client nur sendet, Beendet er sich danach direkt, und schließt damit auch die Verbindung, der Server kommt also nicht zum Lesen, weil die Verbindung direkt wieder geschlossen ist.



Einstein97 hat gesagt.:


> Bisher habe ich wirklich noch keine einfache Client-Server Anwendung gesehen, in der der Client in dieser Weise auf eine Antwort wartet. Man will doch wirklich nur etwas zum Server schicken.


In den meisten Fällen will man auch sichergehen, dass es richtig ankam, und lässt es sich vom Server bestätigen


----------



## Jardcore (22. Jun 2016)

Es wird sogar noch geprüft ob die Daten unterwegs verändert wurden  ... ach was hab ich Rechnernetzte in der Uni gehasst


----------



## Einstein97 (22. Jun 2016)

Mhh okay wird wohl so sein  Jetzt muss ich aber im Server in der receive-Methode auch noch das Zurücksenden einfügen, oder? Also sendMes() auch im Server anwenden...


----------



## Einstein97 (22. Jun 2016)

Guten Abend, ich bin's nochmal
Das Programm läuft jetzt und ich wollte mich für Eure Hilfe und Geduld bedanken.
Eine kleine Frage habe ich aber trotzdem noch. Das Objekt "Test", welches ich losgeschickt habe, kommt zwar beim Server nun an und wird auf der Konsole ausgegeben, jedoch nicht als String, sondern als Message@76a564 oder sowas in der Art...
Hatte nun versucht das Problem mit instanceof in der receive-Methode zu lösen.


```
public void receive(){
        try {
            ob= (Message) in.readObject();
            System.out.println(ob);
            if(ob instanceof String){
                String s= (String) ob;
            }
        } catch (ClassNotFoundException e) {
              e.printStackTrace();
        } catch (IOException e) {
              e.printStackTrace();
        }
}
```
 
Jedoch sagt Java mir dann "cannot cast from Message to String".
Wie kann ich das noch lösen?


----------



## Jardcore (23. Jun 2016)

Wieso willst du das auch tun?  Entweder du implementierst bei Message eine Methode getText() oder getDate() oder ähnlich. Oder du verpasst deinem Message Objekt eine toString() Methode.

"Message@76a564" ist übrigens die Referenz auf dein Message Objekt.

Vielleicht nochmal paar Grundlagen anschauen


----------

