Tutorial: Einfacher Chat mit Node.js und WebSocket

In diesem Node.js-Tutorial entwickle ich eine einfache Webanwendung, in der die Benutzer chatten können. Zum Einsatz kommen dabei Node.js, Soket.IO und jQuery. Als Ergebnis sollen die Benutzer einen Namen wählen, Nachrichten eingeben und absenden, und der Server soll diese mittels einer WebSocket-Verbindung an alle (anderen) Nutzer verteilen können.

Ich werde in folgendem die komplette Anwendung entwickeln, also sowohl den Server als auch den Client, der wiederum aus HTML, JavaScript und CSS besteht. Es geht hierbei in erster Linie um das Verständnis der Funktionsweise und des Entwicklungsprozesses, die Anwendung wird deswegen bewusst einfach und überschaubar gehalten.

Den Quellcode des ganzem veröffentliche ich als Open-Source-Projekt auf Github.

Vorbereitung #
Als erstes erstelle ich die package.json-Datei. in dieser befinden sich Informationen zur Anwendung wie z.B. der Name, sowie die benötigten Node.js-Module. Für diese Anwendung brauche ich genau zwei zusätzliche Module: Express und Socket.io. Die fertige package.json sieht wie folgt aus:

{
„name“: „Node.js-Chat“,
„description“: „Ein einfacher Chat mit Node.js“,
„author“: „NodeCode.de“,
„dependencies“ : {
„express“: „3.x“,
„socket.io“: „*“
}
}
Danach lade ich mir die clientseitige JavaScript-Bibliothek jQuery herunter. Da ich möglichst viele Browser unterstützen möchte, wähle ich die Version 1.x, da diese im Gegensatz zu der 2.x auch den Internet Explorer 6, 7 und 8 unterstützt. Socket.io bietet übrigens Fallback-Alternativen zu WebSocket an, sodass der Internet Explorer ab Version 5.5+ von Socket.io unterstützt wird.

Zudem erstelle ich mir schonmal alle weiteren Dateien, die ich benötigen werden. Alle öffentlichen Daten, die der Webserver zukünftig ausgeben wird, landen im Ordner public. Eine Übersicht aller (wichtigen) Dateien:

package.json – Infos zum Projekt wie der Name und die vom Server benötigten Module
server.js – unser Node.js-Server
config.json – die Konfiguration für den Server
public/ – der Ordner für die öffentlichen Dateien
public/index.html – unsere HTML-Datei
public/style.css – unsere Stylesheet-Datei
public/client.js – unsere clientseitige JavaScript-Datei
public/jquery.min.js – die minifizierte jQuery-Bibliothek
Server #
Der Node.js-Server besteht aus genau zwei Dateien: server.js und config.json. Während server.js der eigentliche Server ist, enthält die config.json die Konfiguration des Servers. Da ich die Anwendung möglichst einfach halte, wird nur der Port mit der config.json konfiguriert. Ich fülle sie deswegen gleich als erstes mit dem Inhalt:

{
„port“: 8080
}
Weiter geht es mit dem eigentlichem Server. Hierbei lade ich in den ersten 5 Zeilen die benötigten Module sowie die soeben erstellte config.json in die Anwendung.

var express = require(‚express‘)
, app = express()
, server = require(‚http‘).createServer(app)
, io = require(’socket.io‘).listen(server)
, conf = require(‚./config.json‘);
Dann binde ich den Webserver an die sich in der Konfigurationsdatei befindende Portnummer:

server.listen(conf.port);
Als nächstes weise ich Express dazu an, Dateien die sich im Ordner public befinden, bei Anfrage an den Besucher auszugeben:

app.configure(function(){
// statische Dateien ausliefern
app.use(express.static(__dirname + ‚/public‘));
});
Und wenn der Pfad / aufgerufen wird, soll die Datei index.html ausgegeben werden:

// wenn der Pfad / aufgerufen wird
app.get(‚/‘, function (req, res) {
// so wird die Datei index.html ausgegeben
res.sendfile(__dirname + ‚/public/index.html‘);
});
Websocket #
Danach kommen wir zum eigentlichem Teil des Servers, nämlich der WebSocket-, oder besser gesagt Socket.io-Verbindung. Sobald sich ein Client verbindet, wird die Nachricht Du bist nun mit dem Server verbunden! gesendet. Der Client wird zukünftig ein Objekt mit den Attributen zeit, text und – sofern es sich nicht um die Verbindungs-Meldung handelt – name erwarten.

Außerdem muss dafür gesorgt werden, dass sobald ein Benutzer etwas sendet, dies über den Server an alle Benutzer verteilt wird. Zuletzt beschriebenes geschieht im folgenden ab Zeile 5:

io.sockets.on(‚connection‘, function (socket) {
// der Client ist verbunden
socket.emit(‚chat‘, { zeit: new Date(), text: ‚Du bist nun mit dem Server verbunden!‘ });
// wenn ein Benutzer einen Text senden
socket.on(‚chat‘, function (data) {
// so wird dieser Text an alle anderen Benutzer gesendet
io.sockets.emit(‚chat‘, { zeit: new Date(), name: data.name || ‚Anonym‘, text: data.text });
});
});
In Zeile 5 warten wir mit der Funktion socket.on() auf eine eingehende Websocket-Nachricht, die den Namen chathat, und binden diese an eine Rückruf-Funktion. Die empfangenen Daten werden dabei in der Variable datagespeichert. Über die Funktion io.sockets.emit() verteilen wir anschließend den aktuellen Zeitstempel, den empfangenen Namen und den empfangenen Text an alle Benutzer, wobei als Name Anonym verwendet wird, wenn kein Name angegeben wurde.

Im Grunde funktioniert der Server ziemlich einfach, denn die Hauptarbeit wird im Client erledigt. Hier noch einmal die gesamten 34 Zeilen der Datei server.js:

var express = require(‚express‘)
, app = express()
, server = require(‚http‘).createServer(app)
, io = require(’socket.io‘).listen(server)
, conf = require(‚./config.json‘);

// Webserver
// auf den Port x schalten
server.listen(conf.port);

app.configure(function(){
// statische Dateien ausliefern
app.use(express.static(__dirname + ‚/public‘));
});

// wenn der Pfad / aufgerufen wird
app.get(‚/‘, function (req, res) {
// so wird die Datei index.html ausgegeben
res.sendfile(__dirname + ‚/public/index.html‘);
});

// Websocket
io.sockets.on(‚connection‘, function (socket) {
// der Client ist verbunden
socket.emit(‚chat‘, { zeit: new Date(), text: ‚Du bist nun mit dem Server verbunden!‘ });
// wenn ein Benutzer einen Text senden
socket.on(‚chat‘, function (data) {
// so wird dieser Text an alle anderen Benutzer gesendet
io.sockets.emit(‚chat‘, { zeit: new Date(), name: data.name || ‚Anonym‘, text: data.text });
});
});

// Portnummer in die Konsole schreiben
console.log(‚Der Server läuft nun unter http://127.0.0.1:‘ + conf.port + ‚/‘);
Client #
Nun beginne ich also mit dem Client. Hier haben wir gleich drei Dateien, die wir füllen müssen: die index.html, style.css und client.js.

HTML5 #
Das Grundgerüst der Webanwendung wird in HTML geschrieben. Im folgenden verwende ich die neuste Version – HTML5, zu erkennen an dem kurzen  und den semantischen HTML-Tags

 und 

.Viel gibt es zur index.html nicht zu sagen, denn es werden im Grunde nur die Eingabefelder, der Senden-Button und die restlichen Elemente erstellt, sowie die style.css und alle JavaScript-Dateien angefordert. Die gesamte index.html sieht wie folgt aus:



Node.js Chat von NodeCode


    Design (CSS) #
    Weiter geht es mit dem Design, der style.css. Hierzu gibt es noch weniger zu sagen, da man Design schlecht in Worte fassen kann. Die Elemente

     und 

     bekommen eine grüne Hintergrundfarbe, und alles wird an die richtigen Pixel gerückt.Die Eingabe-Felder und der Absenden-Button werden (bis in der Größe) im standard-Style des Browsers gelassen, da es sich hierbei um eine Demonstration eines Node.js-Chats handelt, und nicht um einen Design-Wettbewerb. 😀

    Hier noch einmal ein Screenshot der fertigen Anwendung – rechts als Bild, und unten der komplette Inhalt der style.css:

    body{
    margin: 0;
    padding: 0;
    font: 16px „Helvetica neue“, Helvetica, Arial, sans-serif;
    color: #333;
    }
    header, footer{
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: auto;
    padding: 0 0.5em;
    background-color: #8BC84B;
    font-size: 24px;
    line-height: 2em;
    height: 2em;
    color: #fff;
    }
    header{
    font-weight: bold;
    }
    header > a:last-child{
    float: right;
    color: #fff;
    font-weight: normal;
    }
    footer{
    top: auto;
    bottom: 0;
    }
    input{
    height: 1.5em;
    font-size: 14px;
    }
    #content{
    margin: 48px 0.5em;
    list-style: none;
    }
    JavaScript #
    Kommen wir nun zum entscheidenden Teil der Anwendung, dem JavaScript. Das Script soll erst dann ausgeführt werden, soll die Website komplett geladen ist. Daher füge ich als erstes eine Rückruf-Funktion

    $(document).ready(function(){

    });
    ein, welche den nachfolgenden Script umschließt und dann ausgeführt wird, sobald die Website zu Ende geladen hat.

    Jetzt müssen wir zunächst eine Verbindung zum WebSocket-Server herstellen, dies geschieht mit:

    var socket = io.connect();
    Sobald der Client eine Nachricht vom Server erhält, muss diese ausgegeben werden. In diesem Fall wird ein neues Element der Liste #content hinzugefügt. Mehr gibt es dazu nicht zu sagen, außer dass zum Schluss noch nach unten gescrollt wird, sofern dies erforderlich ist.

    socket.on(‚chat‘, function (data) {
    var zeit = new Date(data.zeit);
    $(‚#content‘).append(
    $(‚

    ‚).append(
    // Uhrzeit
    $(“).text(‚[‚ +
    (zeit.getHours() < 10 ? ‚0‘ + zeit.getHours() : zeit.getHours())
    + ‚:‘ +
    (zeit.getMinutes() < 10 ? ‚0‘ + zeit.getMinutes() : zeit.getMinutes())
    + ‚] ‚
    ),
    // Name
    $(‚‚).text(typeof(data.name) != ‚undefined‘ ? data.name + ‚: ‚ : “),
    // Text
    $(“).text(data.text))
    );
    // nach unten scrollen
    $(‚body‘).scrollTop($(‚body‘)[0].scrollHeight);
    });
    Da wir Nachrichten nicht nur empfangen sondern auch senden wollen, definiere ich als nächstes eine Funktion senden(). Diese ließt die Werte der Felder #name und #text aus, und sendet sie anschließen an den Server. Abschließend wird der Inhalt des Feldes #text wieder geleert.

    function senden(){
    // Eingabefelder auslesen
    var name = $(‚#name‘).val();
    var text = $(‚#text‘).val();
    // Socket senden
    socket.emit(‚chat‘, { name: name, text: text });
    // Text-Eingabe leeren
    $(‚#text‘).val(“);
    }
    Die Funktion senden() soll in zwei Fällen aufgerufen werden: Entweder wenn der Button #senden geklickt wird, oder wenn die Enter-Taste innerhalb des Eingabe-Feldes gedrückt wird. Der Code dazu ist ziemlich einfach:

    // bei einem Klick
    $(‚#senden‘).click(senden);
    // oder mit der Enter-Taste
    $(‚#text‘).keypress(function (e) {
    if (e.which == 13) {
    senden();
    }
    });
    Und damit ist auch die client.js fertig. Hier noch einmal der vollständige Code:

    $(document).ready(function(){
    // WebSocket
    var socket = io.connect();
    // neue Nachricht
    socket.on(‚chat‘, function (data) {
    var zeit = new Date(data.zeit);
    $(‚#content‘).append(
    $(‚

    ‚).append(
    // Uhrzeit
    $(“).text(‚[‚ +
    (zeit.getHours() < 10 ? ‚0‘ + zeit.getHours() : zeit.getHours())
    + ‚:‘ +
    (zeit.getMinutes() < 10 ? ‚0‘ + zeit.getMinutes() : zeit.getMinutes())
    + ‚] ‚
    ),
    // Name
    $(‚‚).text(typeof(data.name) != ‚undefined‘ ? data.name + ‚: ‚ : “),
    // Text
    $(“).text(data.text))
    );
    // nach unten scrollen
    $(‚body‘).scrollTop($(‚body‘)[0].scrollHeight);
    });
    // Nachricht senden
    function senden(){
    // Eingabefelder auslesen
    var name = $(‚#name‘).val();
    var text = $(‚#text‘).val();
    // Socket senden
    socket.emit(‚chat‘, { name: name, text: text });
    // Text-Eingabe leeren
    $(‚#text‘).val(“);
    }
    // bei einem Klick
    $(‚#senden‘).click(senden);
    // oder mit der Enter-Taste
    $(‚#text‘).keypress(function (e) {
    if (e.which == 13) {
    senden();
    }
    });
    });
    Schlusswort #
    Nun ist die gesamte Chat-Anwendung fertig. Ich habe vor allem auf der Seite des Servers nur sehr wenige Zeilen Code benötigt, da die meiste Arbeit von den Modulen erledigt wird. Dies ist ein großer Vorteil der Node.js-Module, welcher vor allem bei größeren Anwendungen deutlich sichtbar ist. Man muss sich nicht immer wieder mit grundlegenden Standard-Aufgaben beschäftigen, und kann sich stattdessen gleich auf die eigentlichen Aufgaben der Anwendung konzentrieren. Wahrscheinlich sind alle hiermit geschriebenen Dateien sogar kleiner als der Text dieses Tutorials, den du momentan ließt. Die Code-Boxen logischerweise ausgenommen. 😉

    Du kannst die vollständige Anwendung auf GitHub durchblättern oder dir das Projekt herunterladen. Beachte dabei, dass du nach dem herunterladen noch die nötigen Module installieren musst. Dafür musst du dich mit der Konsoleeinfach in das Verzeichnis der Anwendung begeben und den Befehl npm install eingeben, die benötigten Module werden dabei automatisch aus der package.json ausgelesen und installiert. Um anschließend den Server zu starten, musst du den Befehl node server.js eingeben. Danach ist die Anwendung über die Adresse http://127.0.0.1:8080/ zu erreichen. Genauere Informationen zur Konsole findest du auf der Seite Konsolen Grundlagen.

    Dies war der aufwendigste und zweitintensivste Artikel den ich bisher geschrieben habe, daher interessiert es mich um so mehr ob sich dieser Aufwand geloht hat und mehr solcher Tutorials gewünscht sind. Falls du Fragen hast oder du einen Fehler gefunden hast, kannst du selbstverständlich auch dafür die Kommentar-Funktion unter diesem Artikel nutzen.