# Versuch mit Socket etwas zu lesen und zu schreiben



## BodyLAB (30. Jul 2022)

Hallo zusammen,

ich möchte gerne ein kleinen Taschenrechner Programmieren. Nur ganz simpel doch ich schaffe es nicht mal mit Sockets überhaupt Daten zu Lesen und zu schreiben.
Meine Server Klasse sieht wie folgt aus:

```
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class A1Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket client = null;
        try {
            serverSocket = new ServerSocket(4242);
        } catch (Exception e) {
            System.out.println("Fehler beim Erzeugen des ServerSockets");
            return;
        }

        while (true) {
            try {
                client = serverSocket.accept();
                if (client.isConnected()) {
                    System.out.println("Neuer Spieler connected: " + client.getInetAddress());
                    new A1Handler(client.getInetAddress().getHostName(), client);
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}
```

In dieser Klasse erstelle ich ein Handler Objekt, was die Arbeit erledigen soll. Dort fehlt nun natürlich noch die ganze Logig, da ich leider schon am senden und empfangen von String scheitere  

```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class A1Handler extends Thread {

    Socket client;
    String name;
    BufferedReader in;
    PrintWriter out;

    A1Handler(String name, Socket client) {
        this.name = name;
        this.client = client;
        try {
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            out = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        start();
    }

    public void run() {
        try {
            System.out.println(in.readLine());
            out.write("Test");

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
```

und der Client:

```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class A1Client {
    public static void main(String[] args) {
        System.out.println("Bitte geben Sie eine Funktion ein wie 2x3");

        Socket client = null;
        try {
            client = new Socket("localhost", 4242);
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
            Scanner scanner = new Scanner(System.in);
            String tmp = scanner.next();
            if (tmp != null) {
                out.write(tmp);
            } else
                System.out.println("Fehler");

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
```

Kann man sich das irgendwie vernünftig merken wie das mit den InputStreams auf Seiten Server oder Client erstellt wird? Verstehe da gerade nur Bahnhof.
Des weiteren stehe ich gerade vor dem Problem, was ist wenn sich noch ein PC mit meinem Taschenrechner (später) verbindet. Dann treten derzeit doch sämtliche Probleme auf die man auch aus der Parallelen Programmierung only im PC kennt. Sprich ich müsste die run-Methode synchronisieren im Handler oder? Bzw. dafür sorgen das diese Methode immer nur dann betreten werden darf wenn nicht gerade ein anderer Client am werkeln ist ODER?

Mit RMI möchte ich mich auch noch beschäftigen ;-) doch erst einmal noch Sockets halbwegs verstehen ^.^


----------



## Jw456 (30. Jul 2022)

Hallo wo genau bei was habert es denn?
In dem Kleint sehe ich nicht das du etwas abschickist zb mit flush
In Server kein lesen des inputstream und auch kein flush vom outputstream.


----------



## BodyLAB (30. Jul 2022)

Jw456 hat gesagt.:


> flush


Braucht es flush 

Gut das es euch gibt. Wenn man im Client folgendes schreibt:

```
if (tmp != null) {
                out.write(tmp);
                out.flush();
            } else
                System.out.println("Fehler");
```
Kommen plötzlich Daten am Server an. Also benötigt man flush zum übermitteln der Daten. Ich dachte das wäre so wie in C bei der Ein- und Ausgabe unter Windows mit dem Puffer, aus diesem Grund wurde es von mir ignoriert 🙃



____________
Kann man sich das irgendwie merken wozu welcher Stream ist? Also der InputStream im Client ist für ... der OutputStream für .... 
Der InputStream im Server ist für .... 

Desweiteren würde ich gerne wissen, wie ist es wenn nun mehrere Clients zugriff zum Server haben. Dann benötige ich eine Methode die synchronized ist damit jeder Client nur dann auf dem Server Rechnen darf wenn der erste Client der Rechnen will fertig ist oder :-D


----------



## KonradN (30. Jul 2022)

Input ist Englisch für Eingabe - Also ist der InputStream dazu da, Eingaben (von irgendwo) zu lesen.
Output ist Englisch für Ausgabe - also ist der OutputStream dazu da, Ausgaben (irgendwohin) zu schreiben.

Und das ist immer aus Sicht des aktiven Threads. Also z.B. InputStream im Server: Eingaben lesen. Und hier ist das irgendwo halt vom Socket. Oder OutputStream -> Ausgaben schreiben - und das ist halt dann in / über den Socket.

Flush ist notwendig, denn ansonsten werden Daten so lange zwischengespeichert bis genug Daten da sind um ein Paket voll zu kriegen oder - je nach Methode - gibt es teilweise auch automatisches Absenden. Beim PrintWriter findet sich z.B. die Information:


> Unlike the PrintStream class, if automatic flushing is enabled it will be done only when one of the println, printf, or format methods is invoked, rather than whenever a newline character happens to be output. These methods use the platform's own notion of line separator rather than the newline character.


Da Du kein automatisches Flushen aktiviert hast, ist das aber erst einmal egal.

Generell noch der Hinweis: Du solltest Dir immer ein Protokoll überlegen: Wie sehen die Nachrichten aus. Es gibt keine Trennung. Wenn Du also nacheinander schreibst: "Hallo" "Du" "Ich" "bin" "im" "Java-Forum" "aktiv", dann kommt da wirklich diese Zeichenkette an:
"HalloDuIchBinimJava-Forumaktiv". 
Und das kann in beliebigen Paketen ankommen, Also der Client kann in einer Schleife evtl. lesen:
"HalloDuI"
"chBinimJa"
"va-Forumaktiv"

(Ist jetzt nicht ganz so - die (Tcp/ip) Pakete sind halt durch eine maximale Größe bestimmt, die auch von Netzwerk zu Netzwerk unterschiedlich sein kann. Angegeben als MTU Maximum Transmission Unit)

Daher ist hier immer ein Protokoll notwendig um Pakete zu Kennzeichnen. Da gibt es viele Ideen:

Bei Textbasierten Dingen ist das oft ein Zeilenumbruch
Bei Binary Übertragungen kann man als erstes eine Paketgröße mitteilen oder man hat sowas wie ein Marker, der das Ende kennzeichnet.

Das einfach mal als Hinweis.


----------



## BodyLAB (30. Jul 2022)

KonradN hat gesagt.:


> Daher ist hier immer ein Protokoll notwendig um Pakete zu Kennzeichnen. Da gibt es viele Ideen:
> 
> Bei Textbasierten Dingen ist das oft ein Zeilenumbruch
> Bei Binary Übertragungen kann man als erstes eine Paketgröße mitteilen oder man hat sowas wie ein Marker, der das Ende kennzeichnet.
> ...


Das ist eine sehr gute Idee.
Ich werde erst einmal versuchen den Taschenrechner zu machen. Wenn der läuft, poste ich hier nochmal 
Fragen über Fragen hehe


----------



## Jw456 (30. Jul 2022)

BasBeg hat gesagt.:


> Sprich ich müsste die run-Methode synchronisieren im Handler oder?


Nein die run wird ja nur vom Thread aufgerufen. 
Methoden  Klassen die aus der „run“ aufgerufen werden sollten "Thread sicher" sein.


----------



## BodyLAB (30. Jul 2022)

Also leider klappt es überhaupt nicht!
Finde den Fehler nicht.

*Server*

```
public class A1Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket client = null;
        try {
            serverSocket = new ServerSocket(4242);
        } catch (Exception e) {
            System.out.println("Fehler beim Erzeugen des ServerSockets");
            return;
        }

        while (true) {
            try {
                client = serverSocket.accept();
                if (client.isConnected()) {
                    System.out.println("Neuer Spieler connected: " + client.getInetAddress());
                    new A1Handler(client.getInetAddress().getHostName(), client);
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}
```

*Client*

```
public class A1Client {
    public static void main(String[] args) {
        System.out.println("Bitte geben Sie eine Funktion ein wie 2x3");

        Socket client = null;
        try {
            client = new Socket("localhost", 4242);
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
            Scanner scanner = new Scanner(System.in);
            String tmp = null;
            if ((tmp = scanner.next()) != null) {
                out.write(tmp);
                out.flush();
            } else
                System.out.println("Fehler");

            System.out.println("Das Ergebnis ist: " + in.readLine());
            
            in.close();
            out.close();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
```

*Handler*

```
public class A1Handler extends Thread {

    Socket client;
    String name;
    BufferedReader in;
    PrintWriter out;

    A1Handler(String name, Socket client) {
        this.name = name;
        this.client = client;
        try {
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        start();
    }

    public synchronized void rechnen(String s) {
        int x = Integer.parseInt("" + s.charAt(0));
        char operator = s.charAt(1);
        int y = Integer.parseInt("" + s.charAt(2));
        int erg = 0;

        switch (operator) {
        case '*':
            erg = x * y;
            break;
        case '+':
            erg = x + y;
            break;
        case '-':
            erg = x - y;
            break;
        case ':':
            erg = x / y;
            break;
        default:
            System.out.println("Fehler");
            break;
        }

        out.write(String.valueOf(erg));
        out.flush();
        out.close();
    }

    public void run() {
        try {
            String tmp = in.readLine();
            System.out.println(tmp);
            out.write("Test");
            out.flush();
            
            //rechnen(tmp);
            in.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
```

Ich hab gerade alles weiter auskommentiert, da ich versuche überhaupt einmal ein Test Text von Client an Server und von Server an Client zu senden. Das klappt jedoch einfach nicht!


----------



## KonradN (30. Jul 2022)

Du willst immer ganze Zeile lesen. Aber Du schreibst nie eine ganze Zeile.

Zeile lesen heisst, dass bis zu einem Newline gelesen wird. So ein Newline Zeichen kommt aber nie.

Statt write solltest Du ggf. println verwenden. Alternativ kannst Du auch ein "\n" beim write mitsenden.


----------



## Jw456 (30. Jul 2022)

```
public synchronized void rechnen(String s) {
```
Frage welcher ander Thread greift denn hier zu? Du erstellst doch mit jeden User ein neues Objekt vom "A1Handler"


----------



## BodyLAB (30. Jul 2022)

Jawohl das hat mein Problem gelöst. 



Jw456 hat gesagt.:


> Frage welcher ander Thread greift denn hier zu? Du erstellst doch mit jeden User ein neues Objekt vom "A1Handler"


Das stimmt, doch wie geht das dann anders? Also so das man da etwas synchronisieren muss? Oder ist das üblich so, das immer ein neues Objekt erstellt wird auf allen Servern auf der Welt?


----------



## Jw456 (30. Jul 2022)

In diesem Fall solltest du das synchronized gar nicht brauchen.


----------



## BodyLAB (30. Jul 2022)

Jw456 hat gesagt.:


> In diesem Fall solltest du das synchronized gar nicht brauchen.


Ja, hab es raus genommen. 
Doch ich verstehe gerade nicht mehr wann man in einem Client-Server Umfeld überhaupt syncho benötigt? Kannst du mir vielleicht ein Szenario Skizzieren?


----------



## Jw456 (30. Jul 2022)

falsch


----------



## KonradN (30. Jul 2022)

BasBeg hat gesagt.:


> Das stimmt, doch wie geht das dann anders? Also so das man da etwas synchronisieren muss? Oder ist das üblich so, das immer ein neues Objekt erstellt wird auf allen Servern auf der Welt?


Synchronisieren musst Du nur Zugriffe auf gemeinsame Daten.

Du erzeugst für jeden Client ja mindestens eine Instanz, die alle wichtigen Details hält. Das ist mind. der Socket für den Client (Oder der Channel, wenn Du nio Packages nutzt um nicht die vielen Threads zu haben).

Hier hast Du ja nicht wirklich (gemeinsame) Daten. Du hast ja nur eine Nachricht, die Du dann auswertest um ein Ergebnis zurück zu senden.

Wenn Du eine zentrale Rechner-Instanz hättest, die alle Clients nutzen würden, dann wäre ggf. eine Synchronisierung notwendig. Aber auch das nur, wenn Du den Status der Rechner Instanz veränderst.

Deine Methode Rechnen macht ja nur etwas mit den Parametern / lokalen Variablen. Da wäre eine Synchronisation nie notwendig.



Jw456 hat gesagt.:


> da deine streamvariablen nicht im Thread sind.


Was willst Du mir oder dem TE sagen? Sein Problem, das dann gelöst war, war schlicht und ergreifend die nicht gesendeten Newlines so dass nie eine ganze Zeile gelesen werden konnte.


----------



## Jw456 (30. Jul 2022)

KonradN hat gesagt.:


> Was willst Du mir oder dem TE sagen? Sein Problem, das dann gelöst war, war schlicht und ergreifend die nicht gesendeten Newlines so dass nie eine ganze Zeile gelesen werden konnteich


hab ich gelöscht da falsch ich bin im code verutscht zum Kleint.
du warst zu schnell oder ich zu langsam


----------



## Jw456 (30. Jul 2022)

Synchronisation Thread Sicherheit würdest du brauchen wenn du etwas aufrufst was alle User nutzen können. Zb eine Datenbank die ja dann nicht in dem User-Thread läuft.

 Du hast ja deine gesamte Logik in dem Thread.


----------

