Chat mit Socket.io und Node.js

By | 3. Juni 2016

Ich bin zufällig auf Socket.io gestoßen und wollte mir das Ganze direkt mal anschauen. Socket.io dient in erster Linie der schnelleren Kommunikation zwischen Server und Clients auf Basis von WebSockets.

In diesem Beitrag beschreibe ich die Installation von Socket.io und den Aufbau eines kleinen Chat-Programms auf Basis der gegebenen Demo auf der Socket.io-Website.

Installation

Zuerst wird Node.js benötigt. Es wird heruntergeladen und anschließend installiert:

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

Weiter benötigt man das Package Build-Tools, das man ebenfalls installiert:

sudo apt-get install -y build-essential

Das Framework

Für unser Projekt erstellen wir einen neuen Ordner, z.B. „chat-example“ und erstellen eine package.json

{
"name": "socket-chat",
"version": "1.0.0",
"description": "my first socket.io app",
"dependencies": {}
}

Den Chat wollen wir mithilfe des Express Frameworks erstellen, also:

npm install --save express@4.10.2

Das Server-Script

Wir erstellen eine index.js und tragen folgenden Code ein:

var app = require('express')();
var http = require('http').Server(app);

app.get('/', function(req, res){
  res.send('<h1>Hello world</h1>');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

Der erste Schritt ist getan. Das Script kann nun mit dem Befehl node gestartet werden:

node index.js

Im Browser ist die im Programm definierte Startseite abrufbar.

Socket.io: Hallo Welt!

Der Client

Man könnte den ganzen HTML-Code per res.send() übertragen, was aber bei größeren Seiten schnell unübersichtlich wird. Wir erstellen also eine separate index.html und tragen unsere Website dort ein:

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Dem Server muss mitgeteilt werden, dass er ab sofort die Website aus einer Datei lesen soll. Also ersetzen wir res.send() durch:

res.sendFile(__dirname + '/index.html');

Wir starten das Server-Script erneut und sehen, dass die index.html erfolgreich geladen wurde:

Socket.io: index.html

Kommunikation zwischen Client und Server

Es ist an der Zeit, Client und Server kommunikationsfähig zu machen. Hierbei kommt Socket.io ist Spiel, das wir jetzt installieren:

npm install --save socket.io

Fügen wir nun das Modul zur index.js hinzu:

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendfile('index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

Hier wird auf eingehende Verbindungen gewartet und jeweils eine Meldung auf der Konsole ausgegeben.
Jetzt fehlt nur noch die Anpassung in der index.html:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

Wir starten das Script neu und öffnen die Seite in mehreren Tabs. Bei jeder neuen Verbindung wird eine Meldung auf der Konsole ausgegeben.

Nicht nur eingehende, sondern auch beendete Verbindungen können mit einem Event gebunden werden:

io.on('connection', function(socket){
  console.log('a user connected');
  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

Events abfeuern

Zur Kommunikation zwischen Client und Server werden Events abgefeuert. Daten können als Strings, JSON oder binär übertragen werden. In unserem Beispiel werden wir Strings übertragen.

Für unseren Chat brauchen wir als zuerst ein Event, dass die Nachricht an den Server schickt. Nennen wir es chat message:

<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
</script>

In der index.js empfangen wir das Event:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});

nach einem Start der index.js werden alle Nachrichten, die über den Chat abgesendet werden, auf der Konsole ausgegeben.

Broadcasting

Der Server hat die Nachricht empfangen und muss sie jetzt an alle Clients verteilen:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

Jeder Client benötigt jetzt noch einen Eventhandler:

<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
  socket.on('chat message', function(msg){
    $('#messages').append($('<li>').text(msg));
  });
</script>

Herzlichen Glückwunsch! Das erste Script für Socket.io ist fertig.

Ich habe mir die Mühe gemacht und noch einige Erweiterungen eingebaut:
Socket.io: Erweiterter Chat

Alle User werden jetzt auf der rechten Seite des Fensters aufgelistet. Per Klick auf einen Namen kann eine private Nachricht versendet werden. Private Nachrichten werden im Chat blau hervorgehoben. Außerdem kann man mit „/nick neuername“ den aktuellen Benutzernamen ändern. Smileys werden per Klick in das Textfeld eingefügt. Zuletzt kann der gesamte Chat-Verlauf der aktuellen Session als Textdatei heruntergeladen werden.

Der Quellcode kann auf Github heruntergeladen werden: Download

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert