Websocket Verbindung zwischen Android Client und Erlang Server?
Hallo,
Nachdem ich vor etwa einem Monat damit begann, meine eigene Handyapp zu entwickeln, um meine Kenntnisse zu festigen, bin ich momentan auf folgenden Stand:
Alte Frage: Kommunikation zwischen App und Webserver? (Computer, App, Server) - gutefrage
Ich kann mich mithilfe des HTTP Protokolls bei der App registrieren und anmelden. Kann Freunde hinzufügen und mit denen einen privaten Chat starten. Jedoch funktioniert alles noch über HTTPS und der Client stellt jede sekunde eine Anfrage an den Server. Nun würde ich gerne eine Websocketverbindung implementieren.
Auf den Server läuft Ubuntu, eine SQL Datenbank und ein Apache Webserver. Serverseitig würde ich gerne den Websocketserver mit Erlang programmieren, da bekannte SocialMedia Konzerne dies ebenfalls nutzen. Ich habe mich reichlich über die Websocketverbindung informiert und weis den Ablauf, jedoch finde ich keine ausreichenden Quellen, wie ich den Client in AndroidStudio/Java umsetzen kann, sodass er auch auf Servernachrichten reagiert. (Bisher habe ich nur zum Websocket hin kommunizieren können und direkte Anworten wahrnehmen können), gleicher Effekt wie mit dem HTTP Protokoll. Manche verwenden die Socketbibliothek, welche aber nur direkte Anworten auffangen kann. Andere verwenden SocketIO, dies habe ich aber nicht zum laufen bekommen.
Welche Bibliotheken sollte ich für meine Anforderungen nutzen und in welche Richtung sollte die Umsetzung erfolgen?
Liebe Grüße
Alex
2 Antworten
Serverseitig würde ich gerne den Websocketserver mit Erlang programmieren, (...)
Ich denke, am einfachsten fährst du da auf Basis von Yaws (ein Webserver mit WebSocket-Support) oder Cowboy (siehe SockJS-erlang oder die Implementation von Marcelo Gornstein).
(...) jedoch finde ich keine ausreichenden Quellen, wie ich den Client in AndroidStudio/Java umsetzen kann, sodass er auch auf Servernachrichten reagiert.
Nimm eine Bibliothek wie Java-WebSocket. Die ExampleClient.java zeigt, wie es funktioniert: Die WebSocketClient-Instanz verfügt über eine send-Methode, um Nachrichten an den Server zu schicken und kann in onMessage wiederum empfangene Nachrichten verarbeiten.
Ebenso könntest du die Implementation von JakartaEE verwenden. Im Oracle Blog findest du einen Artikel (How to build applications with the WebSocket API for Java EE and Jakarta EE), der die Nutzung vorstellt.
An sich wäre das möglich, doch du müsstest aufpassen, dass beide auf unterschiedliche Adressen (IP / Port) lauschen. Wenn beide auf dem selben Rechner laufen, müssen sie sich natürlich dessen Ressourcen (Arbeitsspeicher, CPU, etc.) teilen. Für Hobbyprojekte / Projekte mit eher wenigen Nutzeranfragen ist das unproblematisch.
Hallo, ich habe es jetzt versucht, dass ich die Websocketkonfiguration für den Apache Webserver implementiere und anschließend hätte ich den Weg über Cowboy probiert. Ich habe bereits eine Frage bei Stack Overflow erstellt, wo auch Code dabei ist: HTML+Websocket
Wo liegt mein Fehler?
Lauscht ein Server denn bereits auf den Port 12123 und liefert sicher einen Response auf Anfrage (z.B. ein einfaches HTML-Dokument)? Bevor du den Apache als Proxy einrichtest, würde ich das sicherstellen.
Bei deinen Rewrite-Regeln sind mir drei Punkte aufgefallen, die ich ändern würde.
1) Das Protokoll wird mit zwei Slashes beendet, statt nur einem: wss://.
2) Im Upgrade-Header wäre mit nur einem Wert zu rechnen, also wäre ein expliziter einfacher Stringvergleich passender / sicherer.
RewriteCond ${HTTP:Upgrade} =websocket [NC]
3) Der Zugriff auf HTTP-Header wird in der Dokumentation von Apache stets anders gezeigt (sowohl in Beispielen als auch in der Beschreibung - siehe Punkt 4 unter Other things you should be aware of).
%{HTTP:Connection}
Ich denke, es wäre generell einfacher, es erst einmal ohne Zertifikat und TLS über Port 80 zu testen.
<VirtualHost *:80>
ServerName your-domain.de
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:12123/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:12123/$1 [P,L]
ProxyPassReverse / http://localhost:12123/
</VirtualHost>
Auf den Connection-Header muss man meines Erachtens nicht prüfen, denn der Upgrade-Header wäre im Wert bereits eindeutig.
Ich habe zuvor den Webserver für SSL eingerichtet und alles hat mit https://.. auf den Port 443 funktioniert. Dies sollte weiterhin, wenn möglich funktionieren. Wenn ich per Handyapp eine HTTPrequest erstelle, welche auf den 443 Port geht, sollte eine response kommen. Vor der Installation des Sockets hats auch problemlos funktioniert. Bzw. Wenn ich die Zeile RewriteRule auskommentiere, dann funktionierts auch immer noch. Auch mit SSL Zertifikat etc. Ich kann mal deine Änderungen zu Hause probieren. Aber ermöglicht es diese Änderung, dass ich auf dem Port 443 die HTML Seiten aufrufen kann und am Port 12123 die Websocket Verbindung machen kann? Das ist was ich machen will. Oder müsste ich hierfür einen neuen VirtualHost anlegen?
Du sagtest oben "Lauscht ein Server den bereits auf Port 12123" Die HTML Anfragen sollten ja weiterhin auf 443 laufen.
Aber ermöglicht es diese Änderung, dass ich auf dem Port 443 die HTML Seiten aufrufen kann und am Port 12123 die Websocket Verbindung machen kann?
Mein obiges Snippet würde erst einmal keinen Datentransport über wss erlauben.
Du kannst es aber leicht ändern. Tausche die Portnummer 80 gegen 443 aus, ergänze das s für die jeweiligen Protokolle der Adressen und füge die Zertifikatsdirektiven ein.
Du sagtest oben "Lauscht ein Server den bereits auf Port 12123" Die HTML Anfragen sollten ja weiterhin auf 443 laufen.
Dein Apache-Server agiert als Proxy. Das bedeuet, er nimmt HTTP-Anfragen auf Port 80 und 443 an und leitet sie, je nachdem, für welchen Port du nun die VirtualHost-Konfiguration anpasst, an localhost:12123 weiter.
Wenn du also deine Konfiguration für Port 443 setzt und eine Anfrage wie https://your-domain.de/somepage.html an den Server schickst, dann geht diese weiter an https://localhost:12133/somepage.html. Nun muss es natürlich einen Webserver geben, der auf den Host localhost mit dem Port 12133 lauscht und auch antwortet, andernfalls geht die Anfrage ins Leere (und in deinem Serverlog sollte ein Fehler vermerkt werden).
Ach herje. Das wird ja schon sehr kompliziert. Wie kann ich das nun realisieren, das ich sowohl den Websocket jetzt über Port 12123 erreichen kann, aber dennoch meine HTML/PHP weiterhin über z.b.: https://domain.de/login.php erreichen kann. Der Apache ist ja mein Webserver oder nicht? Wie kann ich das jetzt konfigurieren das er diese Seiten erreichbar macht und wo muss ich die Dateien dann abspeichern? Standardmäßig ist dies ja bei /var/www/Domain
Ein Proxyserver ist eigentlich dazu da, den tatsächlichen Webserver mit der Webanwendung weiter abzukapseln. Er kümmert sich um das Weiterleiten von Anfragen, das Stellen eines Zertifikats oder Caching.
Davon bin ich bei meinem obigen Snippet auch ausgegangen.
Die Rolle der Anfragenverarbeitung sowohl für HTTP, als auch WS (Request > Response) hätte der Server übernommen, der auf localhost:12123 lauscht. Ob das nun ein IIS-, Tomcat- oder Express-Server wäre, wäre egal, so lange neben HTTP/S auch WebSockets unterstützt werden. Deshalb gibt es für beide Protokolle eine RewriteRule.
Den Apache sowohl als Proxy, als auch Webserver einzusetzen, sollte ebenso klappen. Wie schon geschrieben, gilt der Proxy nach obiger Konfiguration nur für den entsprechenden VirtualHost-Eintrag / Port. Für localhost:12123 benötigst du einen WebSocket-Server.
Die Proxy-Direktiven (ProxyPass, ProxyPassReverse) können zudem auf einen bestimmten Subpfad beschränkt werden. So würde hier
ProxyPass "/example/" "http://other-server.com/"
eine Anfrage an http://your-domain.com/example/index zu http://other-server-com/example/index umgeleitet werden.
Die Direktiven ProxyPass und ProxyRequests (mit dem Wert Off) habe ich im obigen Snippet übrigens vergessen, sehe ich gerade.
Nutze die Dokumentation von Apache zum mod_proxy-Modul.
Ist dieser Virtualhost dann soweit ok?
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName ***
ServerAdmin webmaster@localhost
DocumentRoot /var/www/***
<Directory /var/www>
Options -Indexes +FollowSymLinks
AllowOverride none
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule .* wss://localhost:12123/$1 [P,L]
ProxyRequests off
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/***/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/***/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/***/chain.pem
</VirtualHost>
</IfModule>
Soweit ich jetzt das richtig verstanden habe, wenn websocket anfragen rein kommen werden diese jetzt durch die RewriteRule auf wss://localhost:12123/$1 weitergeleitet oder?
(...) wenn websocket anfragen rein kommen werden diese (...) weitergeleitet oder?
Theoretisch ja. Praktisch musst du selbst testen, ob mit dieser Konfiguration alles passt.
Ja die HTML Seiten sind soweit noch erreichbar und beim Websocket habe ich noch Schwierigkeiten. Bin nicht in der Lage, deine beiden Cowboy skripe zum laufen zu bringen. Hätte noch ne StackOverflow Frage gestellt, aber dort scheint es, als hätte die Community auch nicht die Ahnung von Erlang die ich benötigen würde :). Ich bin schon am überlegen, ob ich die Websocketserver vorerst nicht über c++ realisiere, andererseits denk ich mir, wenn schon, dann richtig das ichs über Erlang realisieren. Wenn man sich die Vorteile ansieht.
aber dort scheint es, als hätte die Community auch nicht die Ahnung von Erlang die ich benötigen würde
Die Antwort von 7stud ist schon gut, denn sie liefert die richtigen Ansätze.
Die Ausführung von make auf der Konsole erfordert ein entsprechendes Maketool. Der Autor nutzt offensichtlich ein Unix-System, dort ist make bereits integriert. Auf Windows OS kann man sich make via Chocolatey (choco install make) installieren. Die Erlang Shell dürfte aber ebenso ein make-Tool integriert haben. Die Makefile selbst findest du im GitHub-Projekt, sie ist auf die Verzeichnisstruktur des GitHub-Projekts ausgerichtet. Zwingend nutzen musst du sie nicht. Wichtig ist lediglich, das Projekt zu bauen und zu starten. Ob mit einzelnen Befehlen in der Konsole oder via Buildtool / IDE.
IntelliJ kannst du für Erlang-Projekte ebenfalls verwenden. In der JetBrains-Dokumentation wird beschrieben, wie ein Setup aussehen muss.
https://www.jetbrains.com/help/idea/erlang-start-project.html
Bezüglich cowboy_http_handler musst du aufpassen, welche Cowboy-Version du nutzt. Ungefähr ab Version 2 wurden Handler-Namen angepasst. So heißt cowboy_http_handler inzwischen cowboy_handler.
Ich bin schon am überlegen, ob ich die Websocketserver vorerst nicht über c++ realisiere (...)
Nimm den für dich einfacheren Weg. Vorteile einer Technologie bringen dir nur etwas, wenn du in ihr auch bewandert bist und somit weißt, wie du sie für dich einsetzen kannst. Außerdem solltest du eine realistische Einschätzung vornehmen, welche technischen Anforderungen deine Zielanwendung tatsächlich erfüllen muss.
Die Frage ist eher, was verhindert das socket.io läuft . Und benutzt du reines JavaScript vom backend oder ist das Cross Origin mit einem Frontendframework .
also bei mir funktioniert socket.io aufm mobil .
aber ich verstehe auch nicht was du mit direkten antworten etc meinst . es scheint mir das du noch zu wenig verständnis hast von der ganzen thematik . auf was soll den die app sonst reagieren ? sorry aber einerseits sagst du , ja es bekommt nachrichten , anderer seits sagst du nein es bekommt keine nachrichten . meinst du push nachrichten im hintergrund oder mit was ist dein problem vergleichbar .
- Ich benutze kein JavaScript im Backend. Für die HttpRequests habe ich PHP verwendet. Nun möchte ich den Websocket-Server in Erlang schreiben. Ich habe gelesen, dass viele SocialMedia Konzerne auch im Backend/Socket-Server auf Erlang zurückgreifen.
- Soweit ich richtig recherchiert habe funktioniert SocketIO, dass es auf ein event "hört". Wenn z.B. der Server eine "Message" sendet. Jedoch konnte ich bisher noch keinen Weg finden, damit ich mit Erlang einen EventTag z.B. "Message" mit anschließenden "Inhalt" sende.
- Ich konnte mit meinem Programm eine Socket (SocketLibrary )Verbindung erstellen:
private void connect(){
Thread connect = new Thread(new Runnable() {
@Override
public void run() {
Log.e("Socket","Connecting...");
try{
s = new Socket(URL,9000);
Log.e("Socket","Connected!");
} catch (IOException e) {
e.printStackTrace();
}
}
});
connect.start();
}
public Runnable writeToSocket = new Runnable() {
@Override
public void run() {
String message = et.getText().toString();
byte[] a = message.getBytes(StandardCharsets.UTF_8);
DataOutputStream dos;
try {
dos = new DataOutputStream(s.getOutputStream());
dos.write(a);
Log.e("Socket","Writing...");
InputStream r = s.getInputStream();
Log.e("Socket","Receiving...");
int i = r.available();
byte[] data = new byte[80];
r.read(data);
String response = new String(data);
Log.e("Message",response);
} catch (IOException e) {
e.printStackTrace();
}
}
};
Jedoch habe ich hier nur wenn ich die writeToSocket Funktion aufgerufen habe auch eine Antwort vom Server erhalten. Hier mit getInputStream(); Jedoch war es mir nicht möglich eine bidirektionale Verbindung zu realisieren, bei welcher der Serven von sich aus eine Nachricht an den Client senden kann.
sendet den dein server überhaupt was ?
sorry mir ist das noch unklar . also du hast ein connect und erhälst z.b. keine begrüßungs message die du aber im server absetzt ?
also der server muss halt in der connect bzw joined clients ein event senden
und dein client muss halt nach connect auf ein event warten .
da gibts viele hürden woran es scheitern kann .
vielleicht solltest du erstmal mit anderen sprachen die grundlagen aufbauen und dich dann an die umsetzung mit java machen . praktisch sollte doch auch ein javascript client mit deinem server reden können . den soviel ich gelesen hab ist erlangen-server compaitble mit socket.io client javascript .
die frage ist also ob du überhaupt im server ein loop hast der einfach für connected clients überhaupt ein emit macht . wie bei einem game server , der ein loop hat, damit er ständig die aktuellen positionen an den client pusht .
ansonsten ist das meist erstmal das frage anwort spiel , client fragt , server antwortet . ohne loop und eben push nachrichten passiert auch nichts. sind mehrere clients connected, kannd er server einbroadcast emiten und allen clients die nachricht zukommen lassen .
und bidirectional hast du ja schon wenn du auf eine frage eine antwort bekommst, sonst wäre ja eh gar keine connect da , den connect heisst quasi standleitung .
okey, ich probiere es dann mit socketIO noch einmal. Vielleicht habe ich nur den falschen Weg versucht, wenn du sagst es sollte funktionieren. Danke
Vielen Dank! Ich werds morgen gleich probieren. Kann man einen Apache Webserver und einen Yaws Webserver gleichzeitig laufen lassen. Fals ja, Ist dies schlecht bzw. Hat es nachteile?