# Websocket Verständnisfrage



## KIllrogg (11. Aug 2016)

In einem anderem Thread: http://www.java-forum.org/thema/architekturfrage.174012/#post-1097691 habe ich mich mal über andere Wege der Client/Server Kommunikation als RMI erkundigt. 

Für meine Zwecke (man kann sich eine (simple und private) SmartHome Steuerung vorstellen, was den Funktionsumfang angeht) scheint mir die Nutzung von Websockets die richtige Lösung. 

Mittlerweile habe ich mich schon ein wenig eingearbeitet (bin ja nur begeisterter Hobby Programmierer) und es geschafft einen lauffähigen Server und Client zu erstellen wobei der Client auch in der Lage ist Nachrichten (Strings) an den Server zu schicken.

Das Konzept dahinter habe ich aber immer noch nicht ganz verstanden - ich hänge da wohl noch den Interfaces von RMI nach... 

Ich habe nun folgendes PROBLEM: Will ich nun beim Erhalt einer Nachricht Hardware am Server ansteuern kracht mir der Client zusammen weil ich die Klasse für die Hardware im WebSocketAdapter importiere und clientseitig  ist natürlich keine Hardware da... => Chrash!

Exportiere ich den Code auf den Server (*.jar) und lösche dann aus dem Client die Referenzen auf die Hardware und lasse den Client in der IDE laufen erhält der Server die Nachricht und führt auf der HW die Aktionen aus. Der Client rennt natürlich weiter weil er ja keine Ahnung mehr von der HW hat... 

Wie kann ich nun die HW am Server steuern? 

Ich dachte ich schicke einen "Steuerbefehl" (JSON oder sowas) zum Server und der verarbeitet es dann - nur wie?
Oder kann ich hier doch irgendwas ganz anderes machen?

Nachstehend mal meine simple gehaltene Code Basis (die ich aus einem Beispiel habe - siehe Kommentar) + plus Anmerkungen von mir wo ich da was mache..

Ich danke schon im Voraus für alle Ratschläge und bitte gleichzeitig um Hilfe  

Server:

```
package jettyDemo;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class EventServer
/**
 * https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/native-jetty-websocket-example/src/main/java/org/eclipse/jetty/demo
 */

{
    public static void main(String[] args)
    {
        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(8080);
        server.addConnector(connector);

        // Setup the basic application "context" for this application at "/"
        // This is also known as the handler tree (in jetty speak)
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
       
        // Add a websocket to a specific path spec
        ServletHolder holderEvents = new ServletHolder("ws-events", EventServlet.class);
        context.addServlet(holderEvents, "/events/*");

        try
        {
            server.start();
            server.dump(System.err);
            server.join();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}
```




Client: 

```
package jettyDemo;


import java.net.URI;
import java.util.concurrent.Future;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;


public class EventClient
/**
 * https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/native-jetty-websocket-example/src/main/java/org/eclipse/jetty/demo
 */
{
    public static void main(String[] args)
    {
        System.out.println("client gestartet...");
        URI uri = URI.create("ws://localhost:8080/events/");

        WebSocketClient client = new WebSocketClient();
        try
        {
            try
            {
                client.start();
                // The socket that receives events
                EventSocket socket = new EventSocket();
                // Attempt Connect
                Future<Session> fut = client.connect(socket,uri);
                // Wait for Connect
                Session session = fut.get();
                // Send a message
                System.out.println("Client sends a message...");
                session.getRemote().sendString("Hello");
          
//hier habe ich mal getestet wie schnell das Zeug reagiert - geht eh flott :D
      for (int i = 0; i < 100; i++) {
                    System.out.println(i);   
                    session.getRemote().sendString("Hello");
                   
                }

                Close session
                session.close(1000, "Clientside stop");
//                session.close();
            }
            finally
            {
                client.stop();
            }
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}
```

WebsocketServlet: EventServlet

```
package jettyDemo;

import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@SuppressWarnings("serial")
public class EventServlet extends WebSocketServlet
/**
 * https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/native-jetty-websocket-example/src/main/java/org/eclipse/jetty/demo
 * 
 * Abstract Servlet used to bridge the Servlet API to the WebSocket API.
 * To use this servlet, you will be required to register your websockets with the WebSocketServletFactory 
 * so that it can create your websockets under the appropriate conditions.
 */

{
    @Override
    public void configure(WebSocketServletFactory factory)
    {
        factory.register(EventSocket.class);
    }
}
```
Websocketadapter: EventSocket

```
package jettyDemo;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;

public class EventSocket extends WebSocketAdapter
/**
 * https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/native-jetty-websocket-example/src/main/java/org/eclipse/jetty/demo
 * 
 * provides the functionality of the socket connection. 
 */

//hier generiere ich mir meine Hardwareklasse: 
         HardwareKlasse hardware = new HardwarKlasse();

{
    @Override
    public void onWebSocketConnect(Session sess)
    {
        super.onWebSocketConnect(sess);
        System.out.println("Socket Connected: " + sess);
    }
   
    @Override
    public void onWebSocketText(String message)
    {
        super.onWebSocketText(message);
        System.out.println("Received TEXT message: " + message);
//hier mal mein (Pseuo)Code wenn hardware angesprochen werden soll:
               if (message.equals("BefehlIrgendwasZuTun")
               {
                hardware.doSomething
               }
//natürlich läuft der Code so nicht, da die Hardware ja nur am Server liegt. Nehme ich die zwei Codeteile von mir raus und starte den Client neu (in der IDE) und lasse am Server den Code MIT der Hardware gehts wie gewollt. Nur will ich das nicht... ist ja komplett unelegang gelöst... 
    }
   
    @Override
    public void onWebSocketClose(int statusCode, String reason)
    {
        super.onWebSocketClose(statusCode,reason);
        System.out.println("Socket Closed: [" + statusCode + "] " + reason);
    }
   
    @Override
    public void onWebSocketError(Throwable cause)
    {
        super.onWebSocketError(cause);
        cause.printStackTrace(System.err);
    }
}
```


----------



## stg (12. Aug 2016)

KIllrogg hat gesagt.:


> Exportiere ich den Code auf den Server (*.jar) und lösche dann aus dem Client die Referenzen auf die Hardware und lasse den Client in der IDE laufen erhält der Server die Nachricht und führt auf der HW die Aktionen aus. Der Client rennt natürlich weiter weil er ja keine Ahnung mehr von der HW hat...



Willst du denn nicht genau das?


----------



## KIllrogg (12. Aug 2016)

stg hat gesagt.:


> Willst du denn nicht genau das?


Ich will den Effekt, aber nicht den Weg dahin. 
Ich kann doch nicht jedesmal coden -> JAR machen für den Server, alles auf den Server transferieren usw -> dann serverseitigen Code löschen -> JAR für den Client machen, transferieren usw 
um eine lauffähige Anwendung zu haben. 

Ich muss es doch so bauen können, dass ich in der Lage bin vom Client aus dem Server Befehle für die zu geben, ohne diese AUCH am Client zu ausführen zu müssen... 

Was mich (geistig) fertig macht ist folgendes.

Sendet der Client dem Server jetzt eine Textmessage (die eben diverse Befehle beinhalten kann, welche dann serverseitig aufdröseln werde, falls es hier keine schlauere Methode geben sollte):

```
session.getRemote().sendString("left");
```
Dann wird ja anscheinend im  WebSocketAdapter "EventSocket" nachgeschaut was damit passieren soll. 
Interessant ist, dass NUR serverseitig ein sys out erfolgt

```
System.out.println("Received TEXT message: " + message);
```
OK. Alles klar, denk ich mir, hier packst halt die ganzen serverseitigen Aktionen rein, weil den Client interessiert das eh nicht. 
Denkste. Packe ich eben hier noch den Hardware Code rein:

```
if(message.equals("left"))
        {
            object.steerLeft(true);
        }
        if(message.equals("stop"))
        {
            object.steerLeft(false);
        }
```
crasht der Client weil er verusucht die Hardware(Klasse) zu initialisieren, die natürlich nicht dem Client zur Verfügung steht -> ERROR. 



Bei RMI umgeht man das ganze ja mit einem Interface, dass dem Client quasi sagt: schau her ich kann dieses und jenes, wie kann dir egal sein, gib mir den Befehl und der Server kümmert sich drum. 
=> interface hat keine HW Spezifika, client kann damit umgehen. 

Bei websocket hab ich das wohl noch nicht ganz kapiert...


----------



## stg (12. Aug 2016)

Ich verstehe auch ehrlich gesagt deinen Code nicht so richtig. Mag damit zusammenhängen, dass ich mich mit Jetty nicht auskenne ... aber für mich sieht es so aus, als ob du das selbe Socket sowohl als ServerEndpoint als auch als ClientEndpoint verwendest. Das wird in den meisten Fällen nicht sinnvoll sein. 



KIllrogg hat gesagt.:


> Sendet der Client dem Server jetzt eine Textmessage (die eben diverse Befehle beinhalten kann, welche dann serverseitig aufdröseln werde, falls es hier keine schlauere Methode geben sollte)



Im Grunde ist es das, was du anschließend machen musst, ja. Über WebSockets wirst du nichts weiter machen, als Nachrichten verschicken. Der Client sendet eine Nachricht und der Server weiß, was er damit anstellen soll. Direkt Methoden auf dem Server ausführen, wirst du nicht können. Die Kommunikation erfolgt ja ausschließlich über http(s) / ws(s) ... Server und Client müssen nicht einmal in der selben Programmiersprache geschrieben sein, sondern nur das Protokol bedienen.


----------



## KIllrogg (12. Aug 2016)

stg hat gesagt.:


> Ich verstehe auch ehrlich gesagt deinen Code nicht so richtig. Mag damit zusammenhängen, dass ich mich mit Jetty nicht auskenne ... aber für mich sieht es so aus, als ob du das selbe Socket sowohl als ServerEndpoint als auch als ClientEndpoint verwendest. Das wird in den meisten Fällen nicht sinnvoll sein.



haha ich ja auch nicht^^ 
Wie gesagt, hab ich mir diesen (rudimentärsten) Code zum lernen und verstehen geholt. 
Was ich nicht verstehe ist ja, dass der Client explizit den Socket zuweist, aber in der Server Klasse wird der Socket NIE erwähnt aber doch benutzt?!?!

Ich hätte intuitiv auch gedacht, dass es einen Socket(ausgang) beim Client gibt und einen eigenen Socket(eingang) beim Server (und das ganze vice versa weil es ja bi-direktional ist). 
Und ich hab hier (vlt. nur in dem Beispiel Code?) nur einen "universalsocket". 
Jetzt müsste ich also dem Server beibringen einen anderen / eigenen Socket zu verwenden...?



> Im Grunde ist es das, was du anschließend machen musst, ja. Über WebSockets wirst du nichts weiter machen, als Nachrichten verschicken. Der Client sendet eine Nachricht und der Server weiß, was er damit anstellen soll. Direkt Methoden auf dem Server ausführen, wirst du nicht können. Die Kommunikation erfolgt ja ausschließlich über http(s) / ws(s) ... Server und Client müssen nicht einmal in der selben Programmiersprache geschrieben sein, sondern nur das Protokol bedienen.


Ok gut, das war mir im Prinzip eh klar. Vlt. hätte gibt es noch eine schlaue Methode (stadardisierte JSON Formate oder sowas) wie man die Befehle "schön" überträgt. 
Aber das soll mal mein kleinstes Problem sein, irgendeine vernünftige Logik wie ich die zig verschiedenen Befehle ordne/kreiere wird mir dann schon einfallen.


----------



## KIllrogg (17. Aug 2016)

push

mittlerweile hab ich richtig viel gelesen zu dem Thema.
Und es funktioniert immer noch nicht. 
Die meisten Code Beispiele (im Prinzip egtl. alle...) , die ich studiert habe, befassen sich mit dem Klassiker: ein Chatserver. 
Hierbei scheint es ja egal zu sein wenn Client und Server den selben Socket nutzen, da ja beide die selben Funktionen drauf haben (Senden und Empfangen von Text an jeweils alle Teilnehmer der Session...) 

Ich finde leider kein Beispiel, oder eine weiterführende Erklärung, wie man einen Befehl vom Client an den Socket schickt, die dann vom Server empfangen wird und dann NUR VON IHM verarbeitet wird (z.B. Ansteuern von Hardware) OHNE dass dabei der Client durch das Einbinden der Objekte der Hardware im Socket EBENFALLS direkten Zugriff auf die Hardware hat. 

Falls hier jemand ein nettes Beispiel (auf der Basis von embedded Jetty) kennt, wo ich mir das mal an-/abschauen kann, oder einen Rat für mich hat wie man das richtig macht, bin ich unglaublich dankbar!


----------



## stg (18. Aug 2016)

Ich dachte, das hätten wir schon geklärt ... lös dich mal wirklich von allem, was du von RMI und Co. kennst. Der Ablauf sieht ganz (vereinfacht) wirklich so aus:

Client:
`socket.send("A");`

Server:

```
onmessage(String str) {
   if(str.equals("A") doA();
}
```

Du rufst über WebSockets keine Methoden auf dem Server auf, sondern du schickst ihm nur Nachrichten, auf welche dieser dann reagiert. Ob und wie er das tut, darauf hat der Client aber keinen Einfluss. 

Das kann man natürlich aufhübschen, etwa indem man dem Client eine API, die alle verfügbaren Funktionen z.B. als enum vorhält, bereitstellt und Serverseitig eine Map mit den Enum-Values und mit "Referenzen" auf Methoden vorhält oder ähnliches. Am eigentlichen Prinzip ändert sich aber dadurch nix.


----------



## KIllrogg (18. Aug 2016)

HEUREKA!
Danke stq, davon (RMI) hatte ich mich eigentlich gleich schon verabschiedet, als ich websocket (für mich) entdeckt hatte. 

Mein Problem war, dass ich (wie im Beispielcode) im (serverseitigen) Servlet DIESELBE Socketklasse angemeldet hatte, wie ich sie auch clientseitig bekannt gemacht habe. 

Nachdem ich auf diese, feine, Seite gelesen habe, habe ich erst gecheckt, dass man dem Server ja seinen "eigenen Socket" zuweisen kann.
Jetzt läuft die Sache rund - und server und client machen nicht (logischerweise) nicht mehr DASSELBE 

Und gleich nochmal Danke stq - das mit den enums ist schon mal ein feiner "Trick".


----------

