# Client Server -> Server push



## Generic1 (25. Jan 2010)

Hallo,

unterhalb sind 2 ausführbare Programme (funktionieren wirklich), ein Server programm und ein Client programm, der Server wird gestartet und mehrere Clients können sich zum Server verbinden. 
Das Klappt auch alles wunderbar mit den beiden unteren Programmen,

Jetzt wollte ich implementieren, dass, nachdem sich der/die Client zum Server verbunden haben (sagen wir mal 3), der Server Daten entweder zu allen 3 Clients sendet oder nur zu dem Client, zu dem die Daten gehören, Die Daten, die vom Server zu den Clients gesendet werden sollen, hab ich unten mal von der Console eingelesen.

Was mir nicht klar ist, wie mache ich das, dass alle 3 Clients die selbe nachricht bekommen?
Funktioniert das überhaupt?



```
package serialsend;

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

public class Server {

    public Server() throws IOException, ClassNotFoundException, InterruptedException {
        start();
        }

    private void start() throws IOException, ClassNotFoundException, InterruptedException {
        ServerSocket ss = new ServerSocket(3344);
        System.out.println("Server started");
        while(true) {
            final Socket s = ss.accept();
            final ServerThread serverThread = new ServerThread(s);
            serverThread.start();
            }
        }


    private final class ServerThread extends Thread {

        private final Socket s;

        public ServerThread(final Socket s) {
            this.s = s;
            }

        @Override
        public void run() {
            try {
                final ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                final ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
                Object o = null;
                while((o = ois.readObject()) != null) {
                    System.out.println("Am Server empfangen: " + o);
                    oos.writeObject("Hallo vom Server");
                    oos.flush();
                    final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                    String consolenString = "";
                    while((consolenString = br.readLine()) != null) {                                                   
                        oos.writeObject(consolenString);
                        oos.flush();
                        }
                    }
                }
            catch (Exception ex) {
                System.out.println("Ausnahme: " + ex);
                }
            }
        }

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        new Server();
        }
}
```


```
package serialclient;

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

public class Client {

    public Client() throws UnknownHostException, IOException, ClassNotFoundException {
        Socket s = new Socket("localhost", 3344);
        System.out.println("Zum Server verbunden: " + s.isConnected());
        ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
        ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        System.out.println("Client started");
        oos.writeObject("Hallo vom Client init");
        Object o = null;
        while((o = ois.readObject()) != null) {
            System.out.println("Am Client: " + o);
            //oos.writeObject("Hallo vom Client");
            //oos.flush();
            }
        }

    public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {
        new Client();
    }
}
```


----------



## tuxedo (25. Jan 2010)

> Was mir nicht klar ist, wie mache ich das, dass alle 3 Clients die selbe nachricht bekommen?



Am Server eine Liste mit Clients und deren Sockets/Streams pflegen. Wenn eine eingehende Nachricht an alle Clients soll, dann einfach über die Liste iterieren und an jeden in der Liste schicken. 

So schwer ist das doch vom Prinzip her nicht als dass man da nicht drauf kommt, oder?


----------



## Generic1 (25. Jan 2010)

Ne, habs auch gerade hinbekommen, ist meiner Meinung nach aber ein schmutziges Design und sehr schwierig zu testen, aber nagut, vorgaben sind vorgaben.


----------



## tuxedo (25. Jan 2010)

Was ist daran "dirty"? Das Prinzip ist praktikabel und gang und gebe beim Thema "socket".

Wenn du das "schräg" implementierst kann das Prinzip ja nix dafür ;-)


----------



## Generic1 (26. Jan 2010)

Meiner Meinung nach ist das Client - Server Prinzip nicht für einen Push- Betrieb vorgesehen,
Die Streams muss ich "global" definieren damit ich diese in den einzelnen Methoden verwenden kann, man kann nicht einfach aus dem Socket immer wieder den Stream holen.
Daher muss man einen Spagat machen, die Unterstützung für einen Push- Betrieb fehlt meiner Meinung nach.

>> gang und gebe

unter gang ung gebe verstehe ich was anderes, gang und gebe ist, wenn der Client anfrägt und der Server antwortet, dann ist es einfach.


----------



## Gast2 (26. Jan 2010)

Generic1 hat gesagt.:


> Meiner Meinung nach ist das Client - Server Prinzip nicht für einen Push- Betrieb vorgesehen,


ein Server stellt einen Dienst bereit ... wenn der Dienst aber permanent vorsieht das der Server Daten sendet - dann ist das so ... vgl. Videostreaming



> Die Streams muss ich "global" definieren damit ich diese in den einzelnen Methoden verwenden kann, man kann nicht einfach aus dem Socket immer wieder den Stream holen.


deswegen kapsel ich immer einen neuen Client den neuen Socket (via [c]socket.accept()[/c]) in eine eigene Klasse ... da habe ich als Server alles in einer eigenen Client Klasse und brauch mich um den Stream-kram nicht zu kümmern ... und die Clients sammle ich immer fleißig einer Collection



> Daher muss man einen Spagat machen, die Unterstützung für einen Push- Betrieb fehlt meiner Meinung nach.


Sockets dienen zum Datenaustausch zwischen zwei Netzwerkendpunkten - mehr nicht



> unter gang ung gebe verstehe ich was anderes, gang und gebe ist, wenn der Client anfrägt und der Server antwortet, dann ist es einfach.


_Client: "kannst Du das mal an alle anderen Teilnehmer verteilen - danke"_
und? fällt Dir was auf?

hand, mogel


----------



## tuxedo (26. Jan 2010)

@Mogel
FullACK 
Mit ein klein wenig (nicht viel) Aufwand kann man das alles sauber kapseln.

@Generic1

Was glaubst du wie das in C/C++/C#/<beliebige andere Sprache HIER einsetzen> funktioniert? *Exakt genau so wie hier beschrieben.* Und wenn nicht, dann hat einer ne Library drauf aufgesetzt die es einfacher macht. Wenn du natürlich nicht auf die Idee kommst mal nach einer Lib zu schauen, dann musst du's halt selbst machen.

Eine unheimlich praktische Lib die einem die Kommunikation von der Struktur her erleichtert: MINA
Aber wie mit allem: Es wird durch weitere Libs nicht immer weniger Code ;-)

- Alex


----------



## Generic1 (26. Jan 2010)

Nichts anderes hab ich behauptet, man muss sich das ganze selber zusammenstricken um vernünftigen Code nach dem SoC usw. zu bekommen. 
Ein Argument noch warum ich glaube, dass Push nicht unterstützt wird. Probiert mal mit einem Server (Tomcat) einen Pushbetrieb zu realisieren -> das geht nicht.
Bevor jetzt wieder das gelabere mit http, Zustandslosigkeit usw. kommt, im hintergrund wird vom Tomcat auch nichts anderes gemacht, als ein Socket geöffnet usw.

PS. Schau dir mal die C++ Library Boost an. da gibts einiges in diese Richtung.


----------



## tuxedo (26. Jan 2010)

Nicht jedes Protokoll ist push geeignet. Das ist völlig unabhängig von Socket oder nicht-Socket.
HTTP-Push ist nunmal so ein Problemfall. Wie soll der Server denn auch zum Client "pushen" können, wenn der Client die Verbindung nach erfolgreichem abrufen der Seite wieder zumacht und damit die Verbindung (sei es Socket oder sonstwas) wieder beendet. Der http-Server ist nur ein "provider" der seine Clients in erster Linie nicht kennt und auch nicht weiß wo er sie erreichen kann. 

Du kannst also Äpfel (Sockets mit Protokoll A) nicht mit Birnen (Sockets mit Protokoll B) vergleichen.
Ein Socket ist allein nix wert. Erst das Protokoll macht was draus. 

Das vorgehen ist doch beim "verteilen an viele" ganz einfach:

Jeder Client der sich zum Server verbindet landet im Server in einem dafür vorgesehenen COntainer. Über diesen Container kommt man ganz easy an die Streams dran (die intern z.B. nur einmalig vom Socket geholt werden) etc. 
Diese Container sammelst du in einer List. Sieht dein Protokoll nun vor, dass eine am Server eingehende Nachricht an alle anderen geschickt wird, musst du nur aus dem einen COntainer, der die Nachricht empfangen hat auf die Liste mit anderen Conteinern zugreifen, da einmal drüber iterieren und die Nachricht in die Sende-Queue der anderen Container einreihen. Fertig.

Das machst du einmal und bietest dannan zentraler Stelle eine Methode "sendMessageToAllClients(Object message)" an und gut ist. Mit "Aufwand" hat das nix zu tun. Die boost-Lib in der C/C++ Welt ist ja qausi auch (AFAIK) eine 3rd Party lib und gehört nicht zum Standardplattform-SDK, oder irre ich mich da?!

In der Java-Welt gibt es genug Libs die vom Socket abstrahieren (RMI, SIMON, generell RPC) und du dann nix mehr mit Sockets am Hut hast. Und es gibt auch ausreichend Libs die es nach wie vor erlauben auf Socketebene zu coden, aber einem viel Abnehmen (teilw. MINA/NETTY, oder auch xSocket, ...). Man muss halt nur schauen was am besten passt. Für kleine Projekte ist es meist nichtmal nötig eine extra Lib zu nehmen. Das von mir und Mogel beschriebene Vorgehen hat man, wenn man sich mit Sockets und Java etwas auskennt in 5min zusammengetippt (zumindest das Grundgerüst).



- Alex


----------



## Gast2 (26. Jan 2010)

> Bevor jetzt wieder das gelabere mit http, Zustandslosigkeit usw. kommt,


Dir ist aber bewust das bei einem Server-Push via HTTP die Verbindung vom Server nicht getrennt wird?!

anscheinend nicht, mogel


----------



## Generic1 (26. Jan 2010)

Ich wollte nur unterstreichen, dass ein Push nicht üblich ist, ich habs bis jetzt noch nie benötigt und hätte mir auch von den Sockets erwartet, dass man sich die Streams öfter holen kann aber dem ist nicht so. 
Das soll jetzt nicht heißen, dass ich das Gerüst nicht in 5min zusammengebracht habe, aber es erscheint mir trotzdem ein bisschen umständlich, anstatt einfach den Socket herzunehmen und den Stream daraus zu extrahieren eine Collection zu baune in der dann der Stream nur einmal usw...

PS: teile von Boost werden demnächst in C++ integriert, du hast aber recht, momentan muss man diese noch extra dazulinken. 
Und zum Thema Push- Server: jGuru: What is server push? How do I use it from a servlet?


----------



## Gast2 (26. Jan 2010)

Generic1 hat gesagt.:


> Ich wollte nur unterstreichen, dass ein Push nicht üblich ist,


natürlich ist Push üblich


----------



## tuxedo (26. Jan 2010)

Generic1 hat gesagt.:


> Ich wollte nur unterstreichen, dass ein Push nicht üblich ist, ich habs bis jetzt noch nie benötigt und hätte mir auch von den Sockets erwartet, dass man sich die Streams öfter holen kann aber dem ist nicht so.



Wieso unüblich? Auf manchen Protokollen ist es eben aufwendiger oder gar nicht möglich. Nicht alle Protokolle sind push-tauglich. 
Also mit meiner SIMON-Implementierung kann ich PUSH machen. Kein Problem. Der Server kann Events eigenständig an den Client schicken. Und das ist so einfach weil es mein Protokoll vorsieht. 

Ein bestehendes Protokoll für push missbrauchen.. Naja, du hast ja selbst die Situation von HTTP dabei erkannt 

Verstehe aber auch nicht warum du auf push so rumreitest. Da kann der Socket nix für (der kanns ja). Das ist Sache des Protokolls. 



> Das soll jetzt nicht heißen, dass ich das Gerüst nicht in 5min zusammengebracht habe, aber es erscheint mir trotzdem ein bisschen umständlich, anstatt einfach den Socket herzunehmen und den Stream daraus zu extrahieren eine Collection zu baune in der dann der Stream nur einmal usw...



?? Genau so gehts doch... Du steckst den Socket in einen Container. Darin holst du einmal die Streams und legst sie als Membervariablen an. Dann noch Getter dazu und fertig. 
Der ganze Container in ne Colleciton und fertig. 

Da braucht man keine Lib dazu.

- Alex


----------



## Generic1 (26. Jan 2010)

Da sind wir eh gleicher Ansicht, dass man das so machen kann, ich hätte mir Unterstützung gewünscht, damit der Code suaberer wird, gibts aber nicht, deshalb muss mans eben so machen.


----------



## tuxedo (26. Jan 2010)

Na also ... dein Wunsch ist ja fast vergleichbar mit: "Ich hätte mir gewünscht dass System.out.println() von etas einfacherem gekapselt wird..."

Das ist so einfach und doch auch wieder speziell auf die jeweilige Implementierung ausgelegt dass es da halt nix fertiges gibt. 

Jetzt diskutieren wir hier schon "Tage", dabei wär's in 5min implementiert und nach weiteren 20min sauber aufgeräumt.


----------



## Generic1 (26. Jan 2010)

Wenn man verschiedene Ansichten hat, dann kann man wohl diskutieren, ansonsten ist es dir freigestellt ob du in diesen Thread was reinschriebst oder nicht.


----------



## tuxedo (26. Jan 2010)

Versteh mich nicht falsch. Ich schreib gern hier rein. Ich mein nur: Wir haben so viel Zeit für's diskutieren verbracht dass man das Ding locker mehrfach hätte implementieren können


----------



## Generic1 (26. Jan 2010)

tuxedo hat gesagt.:


> Versteh mich nicht falsch. Ich schreib gern hier rein. Ich mein nur: Wir haben so viel Zeit für's diskutieren verbracht dass man das Ding locker mehrfach hätte implementieren können



versteh mich auch nicht falsch aber was glaubst du hab ich nebenbei gemacht, das Teil ist schon lange fertig!! es ist mir nur um eine Diskussion gegangen.
lg


----------



## tuxedo (26. Jan 2010)

Na dann ist ja alles paletti :toll:


----------



## Generic1 (28. Jan 2010)

Hallo nochmals zu diesem Thema,

ein Problemchen hab ich noch und zwar wenn ich am Server die ganzen Sockets der Clients in einem Container pflege, dann ist das ok soweit.
Mein Problem ist jetzt, wenn sich ein Client verabschiedet nach einem Timeout und dann wieder neu verbindet, dann habe ich 2 Sockets in dem Container, beide geben mit bei Socket#isConnected() true zurück. Welchen soll ich dann am Server rauswerfen?
Habt ihr da eine Idee?


----------



## Gast2 (28. Jan 2010)

bei mir gibt es ein entsprechendes Event welche der Server erhält, wenn sich ein Client verabschiedet hat ... dann schmeißt der Server den Client aus der Liste


----------



## Generic1 (28. Jan 2010)

Wenn ich das Netzwerkkabel rausziehe wird sich nicht mehr viel spielen mit einem Event, ich brauche also am Server etwas. damit ich erkenne ob sich der Client verabschiedet hat oder nicht.


----------



## Gast2 (28. Jan 2010)

Du hast also kein Ping-Pong im Protokoll ... es gibt zwar noch andere Möglichkeiten - aber ein eigener Ping-Pong ist am zuverlässigten

Client und Server schicken sich jeweils ein Dummypaket zu ... wenn das Paket vom Server nicht geschickt werden kann, dann ist die Verbindung tot ... bei mir ist im Header die Datenlänge des Paketes enthalten ... wenn die Länge 0 ist, dann ist es das Dummypaket

hand, mogel


----------



## Generic1 (28. Jan 2010)

Ich hab mir jetzt ein Testprogramm geschreiben (Client- Server), beim Client machen ich den Socket nach 5 sec mit "s.close()" zu, am Server habe ich eine Liste mit den Client- Sockets,
Was ich nicht ganz versteh ist, dass TCPIP ein verbindungsorientiertes Protokoll ist und ich trotdem ich am Client den Socket geschlossen habe, am Server bei s.isConnected() -> true bekomme.

Kann sich das jemand erklären? Ich brauche irgendeinen Möglichkeit, dass ich am Server erkenne, ob die Verbindung noch aufrecht ist ohne das ich da ein Protokoll implementiere, Das muss doch gehen mit TCP und Sockets??!!


----------



## tuxedo (28. Jan 2010)

Stichwort TCP-KeepAlive ...

Aber um eine implementierung im Protokoll wirst du nicht rumkommen wenn du zuverlässig und möglichst Zeitnah mitbekommen willst wenn deine Verbindung tot ist (egal in welcher Sprache dein Server/Client geschrieben ist. Nicht immer wird der Client sauber herunter gefahren. setzt ja auch zwischendrin ein Router/Switch aus?!

Kleiner Lese-Tipp: socket : Java Glossary

Gruß
Alex


----------



## Generic1 (28. Jan 2010)

So viel zur Push- Unterstützung in Java aber von mir aus auch in anderen Sprachen, da muss man ein eigenes Protokoll schreiben, damit man mitbekommt, das die Gegenseite nicht mehr da ist,
Auf der Client Seite ist es ja ein leichtes zu dedektieren, ob die Verbindung noch besteht oder nicht, einfach setSOTimeout und die Exception fangen.
Aber danke das du mich in Richtung Push bestätigt hast


----------



## tuxedo (28. Jan 2010)

Du MUSST nicht. Du KANNST. Wenn du einfach das TCP-Keepalive einstellst und das abwartest bekommst du auch "irgendwann" mit wenn die Gegenseite nicht mehr da ist (oder die Verbindung irgendwo zwischendrin unterbrochen wurde). Knackpunkt ist aber das "wann". Das ist nämlich "irgendwann". Du kannst es dummerweise nicht beeinflussen.

Aber wenn du regelmäßig etwas sendest (wenn die Verbindung eh herum idle't), dann ist das quasi ein forcierter Test. Da reicht ein einziges byte. Wenn die Verbindung wirklich tot ist, dann wirst du spätestens 20sec (typischerweise das IO TimeOut) nach dem Senden des test-bytes eine Exception bekommen dass die Verbindung tot ist.


----------



## Gast2 (28. Jan 2010)

Generic1 hat gesagt.:


> So viel zur Push- Unterstützung in Java aber von mir aus auch in anderen Sprachen, da muss man ein eigenes Protokoll schreiben, damit man mitbekommt, das die Gegenseite nicht mehr da ist,


das ist IMO ein Designproblem des TCP-Protokolls ... Du kannst - wie schon von Tuxedo hingewiesen - auf das Signal des Protokolls warten ... aber ein eigenes Protokoll musst Du *immer* definieren ... ob da nun Ping-Pong drinnen vorkommt oder nicht ist Dein Bier (gabs hier nicht mal ein Bier-Smiley?)



> Auf der Client Seite ist es ja ein leichtes zu dedektieren, ob die Verbindung noch besteht oder nicht, einfach setSOTimeout und die Exception fangen.


komisch - geht auf Serverseite genau so ... könnte daran liegen das auf Server Seite auch ein Socket existiert - bin mir aber nicht sicher



> Aber danke das du mich in Richtung Push bestätigt hast


ich habe keine Ahnung was Du mit diesem ominösem PUSH immer hast ... der PUSH existiert nur bei HTTP - der ist dort extra als solches definiert weil HTTP sonst nix kann


----------

