smart | Webentwicklung
Alles rund um HTML5, PHP, WordPress & Co.

HTML5 WebSockets & Ruby-EventMachine

5. April 2012
Stephan
Ruby & Ruby on Rails

Mit HTML5 WebSockets gibt es eine einfache Möglichkeit eine bidirektionale sowie vollduplex-fähige Client-Server-Kommunikation im Web zu ermöglichen. Letztendlich benötigen wir dazu nur einen WebSocket-Server und die clientseitige HTML5 WebSocket-Schnittstelle, welche durch das W3C spezifiziert ist.

Im Ruby-Umfeld haben wir mit der em-websocket-Erweiterung (Gem) einen einfach zu implementierenden WebSocket-Server zur Verfügung.

Anhand dessen soll im Folgenden eine simple Client-Server-Kommunikation auf Basis von HTML5 WebSockets umgesetzt werden. Dabei handelt es sich, wenn man so will, um einen kleinen Chat mit denen die Clients Nachrichten untereinander austauschen können.

WebSocket-Server auf Basis von Ruby-EventMachine

Der folgende Code-Auszug zeigt die grundsätzliche Implementierung des WebSocket-Servers:

require 'rubygems'
require 'eventmachine'
require 'em-websocket'
require 'json'

# Liste mit allen Clients, die verbunden sind
@clients = {}

# EventMachine-Loop starten
EM.run do
	# WebSocket-Server erstellen - Host und Port sind anzupassen
	EM::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |client_websocket|

		# ausgelöst, wenn Verbindung zum Client hergestellt
		client_websocket.onopen do
			client_id = client_websocket.object_id
			puts 'Client ' + client_id.to_s + ' connected'

			if !@clients.include? client_id
				@clients[client_id] = client_websocket
			end
		end

		# ausgelöst, wenn Nachricht vom Client empfangen
		client_websocket.onmessage do |message|
			client_id = client_websocket.object_id
			puts 'From Client ' + client_id.to_s + ' received message: ' + message

			@clients.each do |client_id, client_ws|
				client_ws.send(client_id.to_s + ' sagt: ' + message.to_s)
			end
		end

		# ausgelöst, wenn Verbindung zum Client geschlossen
		client_websocket.onclose do
			client_id = client_websocket.object_id
			puts 'Client ' + client_id.to_s + ' disconnected'

			if @clients.include? client_id
				@clients.delete client_id
			end
		end
	end
end

Dazu eine kurze Erläuterung: Die onopen-Callback-Methode wird immer dann aufgerufen, wenn eine Verbindung zu einem Client hergestellt wurde. Daraufhin speichert der WebSocket-Server lediglich das dazugehörige Client-WebSocket-Objekt in einer Hash-Liste. Analog dazu wird onclose aufgerufen, wenn die Verbindung geschlossen wurde und infolgedessen entfernt der WebSocket-Server das entsprechende Client-WebSocket-Objekt wieder aus der Client-Liste.

Beim Empfang einer Nachricht von einem Client wird onmessage aufgerufen. Hier wird einfach die Client-Liste durchlaufen und an jeden Client wird die empfangene Nachricht gesendet.

Ergänzt sei noch, dass die puts-Anweisungen nur zu Debugging-Zwecken dienen und nur auf Serverseite (Server-Konsole) ausgegeben werden.

HTML-Template

Das dazugehörige HTML-Template könnte so aussehen:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5-WebSocket-Verbindung mittels Ruby-Eventmachine</title>
<link href="css/styles.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="lib/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="js/chat.js"></script>
</head>
<body>
<div id="wrapper">
    <h1>Chat</h1>
    <div id="chatWrap">
        <div id="chatBoxWrap">
            <textarea id="chatBox"></textarea>
        </div>
        <div id="chatSendWrap">
            <input id="chatMessage" type="text" />
            <input id="sendChatMessage" type="button" value="Send Message" />
        </div>
    </div>
</div>
</body>
</html>

Auf dieser Seite haben wir eine Chatbox (Textarea), in der alle Nachrichten angezeigt werden. Zusätzlich gibt es natürlich noch ein Textfeld zur Eingabe einer Nachricht und eine entsprechende Schaltfläche, um die Nachricht abzuschicken. Den Code für die CSS-Datei spar ich mir mal an dieser Stelle.

Clientseitige HTML5 WebSocket-Schnittstelle

Die clientseitige Implementierung in JavaScript sieht wie folgt aus:

$(document).ready(function ()
{
    var webSocket = null;
    var webSocketUrl = 'ws://localhost:8080';

    // prüfen, ob WebSockets vom Browser unterstützt werden
    if(window.WebSocket)
    {
        webSocket = new WebSocket(webSocketUrl);
    }

    webSocket.onopen = function (event)
    {
        $('#chatBox').val('WebSocket-Verbindung hergestellt');
    }

    webSocket.onmessage = function (event)
    {
        // Chatbox aktualisieren und empfangene Nachricht anzeigen
        $('#chatBox').val(
            event.data + '\n' + $('#chatBox').val()
        );
    }

    // für dieses Beispiel sind onerror und onclose nicht relevant
    webSocket.onerror = function (event) { }
    webSocket.onclose = function (event) { }

    // Klick-Event für die Schaltfläche binden
    $('#sendChatMessage').click(function()
    {
        // Nachricht zum WebSocket-Server schicken
        webSocket.send($('#chatMessage').val());
    });
});

Am Anfang wird die WebSocket-Server-Adresse definiert und anschließend das WebSocket-Objekt instanziiert, wodurch die eigentliche WebSocket-Verbindung eingeleitet wird. Dabei sollte man jedoch prüfen, ob der verwendete Webbrowser HTML5 WebSockets unterstützt.

Des Weiteren werden noch die Event-Handler implementiert, wobei in diesem Beispiel nur onopen und onmessage benötigt werden. Die Chatbox wird demnach immer aktualisiert, wenn der Client eine WebSocket-Verbindung erfolgreich hergestellt hat als auch, wenn eine neue Nachricht vom WebSocket-Server empfangen wurde.

Ebenso wurde noch implementiert, dass nachdem ein Client auf die Schaltfläche zum Senden der Nachricht klickt, die eingegebene Nachricht zum WebSocket-Server gesendet wird.

Fazit

Dieses kleine Beispiel zeigt also, dass die Umsetzung einer Client-Server-Kommunikation auf Basis von HTML5 WebSockets in Verbindung mit Ruby-EventMachine bzw. der em-websocket-Erweiterung eigentlich relativ einfach ist.

HTML5 Webockets sind somit finde ich eine gute Alternative zu diversen Plugins, Frameworks und Polling-Techniken, um eine bidirektionale Client-Server-Verbindung herzustellen.

Kennt ihr weitere nützliche Tools, Bibliotheken zur einfachen Implementierung von HTML5 Websockets?

Kommentare  
0 Kommentare vorhanden
1 Trackback/Pingback vorhanden
Du bist herzlich eingeladen auch ein Kommentar zu hinterlassen!
Kommentar schreiben

Vielen Dank für dein Kommentar!