node.js XML-RPC Server

Das Pendant zum XML-RPC Client,
der XML-RPC Server. Dieser nimmt in diesem Beispiel auf Port 1337 ab und verarbeitet die eingehenden Anfragen, wertet diese aus und sendet eine Antwort zurück.

 

Beispiel Script

/* Das Modul laden */
var fs = require('fs')
  , xmlrpc = require('./xmlrpc/lib/node-xmlrpc.js')

/*
XML-RPC Server
*/

/*
Die verschiedenen Inhalte der Anfragen werden in verschiedenden Objekte hinterlegt.
*/
var serverContents = {
  calls: []
, arrayValue: null
, booleanValue: null
, dateTimeValue: null
, doubleValue: null
, integerValue: null
, stringValue: null
, structValue: null
}

// XML-RPC Server erstellen
var serverOptions = {
  host: 'localhost'
, port: 1337
}
// Hierbei kann man wie beim Client auch einen String fuer den Connect uebergeben.
// var serverOptions = 'http://localhost:9090'
var server = xmlrpc.createServer(serverOptions)

// Einen HTTPS XML-RPC Server starten
/*
var secureServerOptions = {
host: 'localhost'
, port: 443
, key: fs.readFileSync('./test-key.pem')
, cert: fs.readFileSync('./test-cert.pem')
}
var server = xmlrpc.createSecureServer(secureServerOptions)
*/

// Methoden fuer die verschiedenen Anfragen
// Arrays
// 'setArray' is the method call to listen for
server.on('setArray', function (err, params, callback) {
  serverContents.calls.push('setArray')
  serverContents.arrayValue = params[0]
  callback()
})
server.on('getArray', function (err, params, callback) {
  serverContents.calls.push('getArray')
  callback(null, serverContents.arrayValue)
})
// Boolesche Werte
server.on('setBoolean', function (err, params, callback) {
  serverContents.calls.push('setBoolean')
  serverContents.booleanValue = params[0]
  callback()
})
server.on('getBoolean', function (err, params, callback) {
  serverContents.calls.push('getBoolean')
  callback(null, serverContents.booleanValue)
})
// Datumswerte
server.on('setDate', function (err, params, callback) {
  serverContents.calls.push('setDate')
  serverContents.dateValue = params[0]
  callback()
})
server.on('getDate', function (err, params, callback) {
  serverContents.calls.push('getDate')
  callback(null, serverContents.dateValue)
})
// Werte vom Typ Double
server.on('setDouble', function (err, params, callback) {
  serverContents.calls.push('setDouble')
  serverContents.doubleValue = params[0]
  callback()
})
server.on('getDouble', function (err, params, callback) {
  serverContents.calls.push('getDouble')
  callback(null, serverContents.doubleValue)
})
// Integer Werte
server.on('setInteger', function (err, params, callback) {
  serverContents.calls.push('setInteger')
  serverContents.integerValue = params[0]
  callback()
})
server.on('getInteger', function (err, params, callback) {
  serverContents.calls.push('getInteger')
  callback(null, serverContents.integerValue)
})
// String Werte
server.on('setString', function (err, params, callback) {
  serverContents.calls.push('setString')
  serverContents.stringValue = params[0]
  callback()
})
server.on('getString', function (err, params, callback) {
  serverContents.calls.push('getString')
  callback(null, serverContents.stringValue)
})
// Strukturen/Objekte
server.on('setStruct', function (err, params, callback) {
  serverContents.calls.push('setStruct')
  serverContents.structValue = params[0]
  callback()
})
server.on('getStruct', function (err, params, callback) {
  serverContents.calls.push('getStruct')
  callback(null, serverContents.structValue)
})
// Return a fault message
server.on('fakeFault', function (error, params, callback) {
  serverContents.calls.push('fakeFault')
  callback({ faultCode: 2, faultString: 'Uh oh.'}, null)
})

node.js XML-RPC Client

Da ich momentan eine Lösung gesucht habe um aus node.js eine XML-RPC Konforme Nachricht zu senden, stiess ich auf folgenden Ansatz.
Das Script für den XML-RPC Server ist hier zu finden.

 

Zuerst wird das modul “xmlrpc” benötigt. Dieses laden wir mit dem NPM Paketmanager nach.

npm install xmlrpc

Das benötigte Modul findet man nun unter

xmlrpc/lib/

Beispiel Script

/*
XML-RPC CLient
*/
/* Modul laden */
var fs = require('fs')
  , xmlrpc = require('./xmlrpc/lib/node-xmlrpc.js')

 // XML-RPC Client Optionen festlegen
  var clientOptions = {
    host: 'localhost'
  , port: 1337
  , path: '/'
  }
  // Man kann die Optionen auch als String uebergeben
  // var clientOptions = 'http://localhost:1337'
  var client = xmlrpc.createClient(clientOptions)

// HTTPS kann folgendermassen verwendet werden
/*
var secureClientOptions = {
host: 'localhost'
, port: 443
, path: '/'
}
var client = xmlrpc.createSecureClient(secureClientOptions)
*/

  client.methodCall('setArray', [['value1', 'value2']], function(error, value) {
    client.methodCall('getArray', null, function (error, value) {
      console.log('Get Array Response: ' + value)
    })
  })

/*
Verschiedene Beispiel Methoden
*/

// Einen Booleschen Wert senden
 client.methodCall('setBoolean', [true], function (error, value) {
    client.methodCall('getBoolean', null, function (error, value) {
      console.log('Get Boolean Response: ' + value)
    })
  })

// Ein Datumswert senden
  client.methodCall('setDate', [new Date(2016, 05, 08, 11, 35, 10)], function (error, value) {
    client.methodCall('getDate', null, function (error, value) {
      console.log('Get Date Response: ' + value)
    })
  })

  client.methodCall('setDouble', [24.99], function (error, value) {
    client.methodCall('getDouble', null, function (error, value) {
      console.log('Get Double Response: ' + value)
    })
  })

// Einen Integer Zahlenwert senden
  client.methodCall('setInteger', [23], function (error, value) {
    client.methodCall('getInteger', null, function (error, value) {
      console.log('Get Integer Response: ' + value)
    })
  })

// Einen String senden
  client.methodCall('setString', ['testString1'], function (error, value) {
    client.methodCall('getString', null, function (error, value) {
      console.log('Get String Response: ' + value)
    })
  })

// Mehrere Werte als Object definieren
  client.methodCall('setStruct', [{ nameOfValue: 'Go 1998!' }], function (error, value) {
    client.methodCall('getStruct', null, function (error, value) {
      console.log('Get Struct Response (on next line): ')
      console.log(value)
    })
  })

// Einen null Wert senden
 client.methodCall('fakeFault', null, function (error, value) {
    console.log('Fake Fault Response as Error (on next line): ')
    console.log(error)
  })

Linux Shell – String ersetzen

Wenn man einmal vor dem Problem steht einen String in einer oder mehreren Dateien zu ersetzen, hilft einem folgender Shell Befehl.

String in einer Datei ersetzen:

sed -i "s/ALTERSTRING/NEUERSTRING/g" datei.txt

String in mehreren Dateien mit der Endung txt im selben Verzeichniss ersetzen:

sed -i "s/ALTERSTRING/NEUERSTRING/g" *.txt

String in einer Datei suchen und in eine neue Datei schreiben und ersetzen:

sed "s/ALTERSTRING/NEUERSTRING/g" datei.txt > datei_neu.txt

Das “s” steht in diesem Falle für “substitute” (Ersatz) sowie das “g” für “global”. Würde man das “g” weg lassen, würde pro Zeile nur ein String ersetzt!

node.js TCP/IP Socket Server + MySQL Datenbank

Nachdem im vorherigen Blogpost node.js modul db-mysql Installation das Modul db-mysql installiert wurde. Benutzen wir dieses Modul nun in dem Beispiel Code.

 

 

 

In diesem Beispiel wird ein TCP/IP Socket Server erstellt, der auf Port 8000 lauscht. Dieser kann eine MySQL Datenbank Verbindung aufbauen und Daten in der Datenbank eintragen. Alle Eingaben die vom User mit Enter bestätigt werden, speichert dieser Server in der Datenbank.

MySql Tabellenstruktur:

Tabellenname: test
id, nachricht

Um auf das Modul “db-mysql.js” zuzugreifen, kopieren wir dieses und die erforderlichen kompilierten node Datenbanktreiber aus dem vorherigen Blogpost in unser Script Verzeichniss.

cp -R DB-MYSQL-VERZEICHNISS/node_modules/db-mysql/build /SCRIPTVERZEICHNISS
cp DB-MYSQL-VERZEICHNISS/node_modules/db-mysql/db-mysql.js /SCRIPTVERZEICHNISS

Nun erstellt man wie gehabt eine Textdatei.

vim socket-server-mysql.js

Für dieses Beispiel benötigt man zwei node.js module

var net = require('net');
var mysql = require('./db-mysql.js');

Die Funktion um einen neuen Datanbankeintrag zu erzeugen erstellen wir zu oberst. Wenn die Funktion später aufgerufen wird. Speichert diese den Wert von “name” in der Datenbank.

function insert_data(nachricht){
new mysql.Database({ // Neue mysql Datenbankverbindung aufbauen
    hostname: 'localhost',
    user: 'DBUSER', // Datenbank Benutzername
    password: 'DBPASS', // Datenbank Passwort
    database: 'DBNAME' // Datenbankname
}).connect(function(error) {
    if (error) {
        return console.log('CONNECTION error: ' + error);
    }
    this.query(). // mySQL Query erstellen
        insert('test', // Tabelle 'test'
            ['nachricht'],  // Tabellenspalte 'nachricht'
            [nachricht]
        ).
        execute(function(error, result) { // mySql Query ausführen
                if (error) {
                        console.log('ERROR: ' + error);
                        return;
                }
                console.log('Inserted id: ' + result.id);
        });
});
}

Nun wird nur noch der Codeteil für den TCP/IP Socket benötigt. Sobald der User eine Nachricht an den Server schickt und diese mit EINGABE bestätigt, wird die gesendete Nachricht in die Datenbank geschrieben. Hierbei ist noch zu beachten das der “Carriage return” mit “\n\r” erkannt wird!

var server = net.createServer(function(socket){

        var sock_adr = socket.address();

        socket.write('Welcome on our Server. Port: ' + sock_adr.port);
        console.log('Socket open at' + sock_adr.port + '\n');

        var ausgabe = "";

        socket.on('data', function(data){ /* Ankommende Daten verarbeiten */

                socket.setEncoding("utf8"); /*Encoding ausgabe auf utf8 setzen*/
                ausgabe += data;
                        if(data == "\r\n"){ /*Wenn ENTER Usereingabe in DB schreiben! Carriage Return \r\n !!!*/
                              console.log(ausgabe);
                              insert_data(ausgabe); // Nachricht in Datenbank schreiben.
                              ausgabe = "";
                        }
        });
});

server.listen(8000,"0.0.0.0");

Nun wird der Server gestartet

node socket-server-mysql.js

Um eine Verbindung mit dem Server herzustellen habe ich vorzugsweise Telnet benutzt.

telnet SERVERADRESSE 8000

Ist man verbunden sollte der Server als erstes mit folgender Nachricht antworten

Welcome on our Server. Port: 8000

Tippt man nun etwas ein und bestätigt die Eingabe mit ENTER. Wir die Nachricht in der Datenbank gespeichert.
Leereingaben werden nicht abgefangen und landen auch direkt in der Datenbank. Dies könnte man noch vervollständigen.

Komplettes script:

var net = require('net'); // TCP/IP Networking Modul
var mysql = require('./db-mysql.js'); // mySQL Modul

function insert_data(nachricht){
new mysql.Database({ // Neue mysql Datenbankverbindung aufbauen
    hostname: 'localhost',
    user: 'DBUSER', // Datenbank Benutzername
    password: 'DBPASS', // Datenbank Passwort
    database: 'DBNAME' // Datenbankname
}).connect(function(error) {
    if (error) {
        return console.log('CONNECTION error: ' + error);
    }
    this.query(). // mySQL Query erstellen
        insert('test', // Tabelle 'test'
            ['nachricht'],  // Tabellenspalte 'nachricht'
            [nachricht]
        ).
        execute(function(error, result) { // mySql Query ausführen
                if (error) {
                        console.log('ERROR: ' + error);
                        return;
                }
                console.log('Inserted id: ' + result.id);
        });
});
}

var server = net.createServer(function(socket){

        var sock_adr = socket.address();

        socket.write('Welcome on our Server. Port: ' + sock_adr.port);
        console.log('Socket open at' + sock_adr.port + '\n');

        var ausgabe = "";

        socket.on('data', function(data){ /* Ankommende Daten verarbeiten */

                socket.setEncoding("utf8"); /*Encoding ausgabe auf utf8 setzen*/
                ausgabe += data;
                        if(data == "\r\n"){ /*Wenn ENTER Usereingabe in DB schreiben! Carriage Return \r\n !!!*/
                              console.log(ausgabe);
                              insert_data(ausgabe); // Nachricht in Datenbank schreiben.
                              ausgabe = "";
                        }
        });
});

server.listen(8000,"0.0.0.0");

node.js modul db-mysql Installation

Ein Web Server oder generell ein Server, worin Daten für längere Zeit abgelegt werden können musste her.

Da node.js ein sehr neues Arbeitswerkzeug ist, sind Datenbanktreiber für bestimmte Kommerzielle Datenbanksysteme rar gesäht.
MySQL Datenbanktreiber sind stattdessen einige vorhanden und ich habe mich hierbei für “db-mysql” entschieden.
Mir gefiel der Objektaufbau bei MySQL Anfragen und dessen einfache verwendung.

Um sich die Arbeit zu erleichtern werden wir das Modul “db-mysql” über den npm (Node Packet Manager) installieren.

curl http://npmjs.org/install.sh | sh

Um das modul “db-mysql” nutzten zu können benötigen wir die MySQL Client developer Librarys.

apt-get install libmysqlclient-dev

Nun müssen wir überprüfen ob die Environment Variable zur mysql_config stimmt. Und das modul diese findet.

echo $MYSQL_CONFIG
/usr/bin/mysql_config <-- Ausgabe

Wird die Ausgabe “/usr/bin/mysql_config” angezeigt ist die Environment Variable korrekt gesetzt. Der Ausgabepfad kann sich von Linux zu Linux System unterscheiden!.

Wird stattdessen nichts ausgegeben, muss die Environment Variable auf den config Pfad  gesetzt werden. Wenn man sich nicht sicher ist wo die mysql_config steckt, führt man eine Suche aus.

find / -name "mysql_config"

Nachdem man nun herrausgefunden hat wo die mysql_config steckt, wird der Pfad in der Environment Variable hinterlegt.

export MYSQL_CONFIG=/usr/bin/mysql_config

Jetzt sind alle Abhängigkeiten gegeben und man kann das modul “db-mysql” mit npm installieren.

npm install db-mysql

Weitere Informationen zu db-mysql sind auf der Entwickler Homepage zu finden.
http://nodejsdb.org/db-mysql/

node.js – Eine neue alte Welt

Nachdem am 04.11.2011 Version 0.6.0 stable von node.js released wurde. Habe ich dies als Grund genommen um das Update einzuspielen und mich mal genauer mit der Materie zu beschäftigen.

Node.js baut auf der von google entwickelten V8 Javascript Engine auf, die auch in jedem Chrome Client arbeitet.
Sehr interessant ist das Connection-Handling von node.js. Hierbei wird nicht wie bei anderen Applikationen für jede Verbindung ein eigener Thread erstellt sondern es läuft in der Virtuellen V8 Engine mit nur einem Thread.
Das ist für den Webserver schon mal von Vorteil bei vielen einzelnen Threads per Verbindung, da diese Threads Arbeitsspeicherfresser sind. Und wir nicht unbegrenzt von diesen zur Verfügung haben.
Bei node.js wird dieses Problem Event basiert gelöst. Alle I/Os sind Event basiert und asyncron. Somit wird der Server oder die Software in einem Thread nicht auf die Ausführung des Codes warten. Der Server kann weiterhin I/Os entgegennehmen und abarbeiten.

Mich hat erstaunt, das es zum Beispiel möglich ist mit wenig Code minimale  Web Server oder TCP/IP Sockets in einem schlanken Framework zu bauen. Ich fand die Thematik bei Python und dessen umfangreichen Librarys schon interessant mit wenig Code grossartige Ergebnisse zu erzielen.
Dennoch finde ich den Gedanken interessant, Serverseitig sowie Clientseitig die selbe Scriptsprache zu benutzten. Allein im Team lassen sich so einige Fallstricke beseitigen. Da auf beiden Seiten der gleiche Code verwendet wird.

Als kleines Beispiel werde ich hier die Installation und Konfiguration erläutern. Sowie kleine Beispiele anhängen.

User Testsystem ist ein Debian 5 Lenny 64bit Server.

1. Zuerst besorgen wir uns die neuste Version der node.js Umgebung.
http://nodejs.org/#download

oder direkt auf der Console:

wget http://nodejs.org/dist/v0.6.0/node-v0.6.0.tar.gz

2. und entpacken die soeben runtergeladene Datei.

tar -xzvf node-v0.6.0.tar.gz

In diesem File befinden sich nun alle erforderlichen Dateien um die ersten Gehversuche mit node.js zu starten.
(Nicht direkt unter root installieren! Vorher auf jeden fall einen eigenständigen User für node.js erstellen!)

3. Nun wechseln wir in das so eben entpackte Verzeichniss und kompilieren die Sources. Configure wird anzeigen wenn Abhängigkeiten nicht gegeben sind. Diese müssen dann vorher installiert werden. Das versteht sich hoffentlich von selber.

./configure

Checking for program g++ or c++          : /usr/bin/g++
Checking for program cpp                 : /usr/bin/cpp
Checking for program ar                  : /usr/bin/ar
Checking for program ranlib              : /usr/bin/ranlib
Checking for g++                         : ok
Checking for program gcc or cc           : /usr/bin/gcc
Checking for gcc                         : ok
Checking for library dl                  : yes
Checking for openssl                     : not found
Checking for function SSL_library_init   : yes
Checking for header openssl/crypto.h     : yes
Checking for library util                : yes
Checking for library rt                  : yes
Checking for fdatasync(2) with c++       : yes
'configure' finished successfully (3.005s)
make
make install

Wenn das Kompilieren dann abgeschlossen ist und node.js installiert wurde. Kann man auch schon direkt auf der Console loslegen und das ganze einmal testen.

node

Mit “node” und ohne jegliche Argumente zu übergeben, gelangt man auf die Eingabeoberfläche von node.js.
Hier kann man direkt Javascript Befehle eingeben und bei bestätigen mit Enter den Code ausführen lassen.

var a = 1; var b = 2; a + b;

3

Oder ein TimerEvent beispiel. Das nach 2000ms(2sec) Hallo Systemwebservice ausgibt.

setTimeout(function(){
console.log('Hallo Systemwebservice');
},2000);

 

Da dies alles sehr simple Beispiele sind, werden wir nun ein wenig konkreter. Hierzu erstellen wir eine Textdatei. Ich werde dies hier mit “vim” machen. Dazu kann natürlich jeder beliebige Texteditor verwendet werden.

vim webserver-test.js

In dieser Textdatei tragen wir nun unseren Code für einen minimal Web Server ein. Der Anfragen auf Port 8000 entgegennimmt und mit “Hallo Systemwebservice” beantwortet.

var http = require('http');

Hiermit wird die benötigte Library für das HTTP Protokoll geladen und direkt der Variable “http” übergeben. Somit müssen wir das Objekt nicht in einem späteren Zeitpunkt laden.

http.createServer(function (request, resource) {
  resource.writeHead(200, {'Content-Type': 'text/plain'});
  resource.end('Hallo Systemwebservice\n');
}).listen(8000, "0.0.0.0");

Nun erstellen wir den Server auf Port 8000 und der Adresse 0.0.0.0.
Nach erfolgreichem erstellen des Servers wird mit “resource.writeHead(200,{‘Content-Type’, ‘text/plain’});” der Header der Nachricht geschrieben.
“resource.end(‘Hello World’);” schreibt den body, sendet diesen zum Benutzer und beendet die Anfrage.

console.log('Server gestartet http://0.0.0.0:8000/');

Hiermit geben wir aus das der Server läuft.

Das komplette Script:

// Library laden
var http = require('http');

http.createServer(function(request, resource){
        // Header schreiben
        resource.writeHead(200, {'Content-Type':'text/plain'});

        //Body schreiben und beenden
        resource.end('Hallo Systemwebservice');
}).listen(8000,"0.0.0.0");

console.log("Server gestartet http://0.0.0.0:8000/");