| | |
| | | </section> |
| | | <section> |
| | | <section> |
| | | |
| | | <h2>NodeJS</h2> |
| | | </section> |
| | | <section> |
| | | <h3>Was ist NodeJS?</h3> |
| | | <ul> |
| | | <li class="fragment">"JavaScript auf dem Server"</li> |
| | | <li class="fragment">basiert auf der V8-Engine von Google</li> |
| | | <li class="fragment">Laufzeitumgebung für JavaScript mit eigener API</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <img data-src="/assets/images/node-js.svg"> |
| | | </section> |
| | | <section> |
| | | <h3>Überblick über die Node-API</h3> |
| | | <p>Die API ist in viele verschiedene Module aufgeteilt</p> |
| | | <table> |
| | | <tr><td>fs</td><td>Dateisystem</td></tr> |
| | | <tr><td>crypto</td><td>Kryptografie</td></tr> |
| | | <tr><td>zlib</td><td>Datenkompression</td></tr> |
| | | <tr><td>http</td><td>Webserver</td></tr> |
| | | <tr><td>net</td><td>Netzwerk (TCP)</td></tr> |
| | | <tr><td>tls</td><td>Verschlüsseltes Netzwerk (TCP)</td></tr> |
| | | </table> |
| | | </section> |
| | | <section> |
| | | <p>Aufruf eines Programmes in der Kommandozeile</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | node [Node-Parameter] path/to/script.js [Anwendungsparameter] |
| | | </code> |
| | | </pre> |
| | | <p>z.B.</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | node --inspect index.js --port 3000 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Einbinden von Modulen</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const fs = require("fs"); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufbau eines Moduls</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | module.exports = { |
| | | foo: 5, |
| | | sayHello: function(name) { |
| | | return "Hello " + name + "!"; |
| | | } |
| | | } |
| | | </code> |
| | | </pre> |
| | | <p>oder</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | exports.foo = 5; |
| | | exports.sayHello = function(name) { |
| | | return "Hello " + name + "!"; |
| | | }; |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Schreibe ein Modul, welches eine <code>Square</code>-Klasse zur Verfügung stellt, mit welchem man den Umfang und den Flächeninhalt eines Rechtecks berechnen kann:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const Square = require("./square"); |
| | | |
| | | const square = new Square(2, 3); |
| | | |
| | | console.log(square.getArea()); // => 6 |
| | | console.log(square.getPerimeter()); // => 10 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Module für die Kommandozeile</h3> |
| | | <p>package.json</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm init -y |
| | | </code> |
| | | </pre> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | { |
| | | ... |
| | | "bin": { |
| | | "my-command": "./index.js" |
| | | } |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>index.js</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | #! /usr/bin/env node |
| | | |
| | | console.log("Hello world from the command line!"); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Installation</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm install -g path/to/project |
| | | </code> |
| | | </pre> |
| | | z.B. |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | npm install -g . |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Ausführung</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | my-command |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Kommandozeilenparameter</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | console.log(process.argv); |
| | | </code> |
| | | </pre> |
| | | <p>angelehnt an die Parameter aus der Programmiersprache <code>c</code>:</p> |
| | | <pre> |
| | | <code class="c" data-trim data-line-numbers> |
| | | int main(int argc, char *argv[]) |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Mache das vorherige Programm für die Konsole aufrufbar, sodass die Länge und Breite für das Rechteck per Konsolenargumente übergeben werden können und der Flächeninhalt ausgegeben wird:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | my-command 2 3 |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Buffer</h3> |
| | | <p>Array-artige Struktur für rohe Bytes</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const b1 = Buffer.from("abc"); // bevorzugt |
| | | const b2 = Buffer.alloc(100, 0); // Länge mit initialen Daten |
| | | const b3 = Buffer.allocUnsafe(100); // Länge ohne initiale Daten |
| | | const b4 = new Buffer("abc"); // offiziell nicht empfohlen |
| | | |
| | | console.log(b1); // => <Buffer 61 62 63> |
| | | console.log(b1.toString("hex")); // => "616263" |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Mit Buffern arbeiten:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const a = Buffer.from("a") + Buffer.from("b"); |
| | | const b = Buffer.concat([Buffer.from("a"), Buffer.from([98])]); |
| | | const c = b.toString() |
| | | |
| | | console.log(a); // => ab |
| | | console.log(b); // => <Buffer 61 62> |
| | | console.log(c); // => ab |
| | | |
| | | for (const byte of a) { |
| | | console.log(byte); // => 97 98 |
| | | } |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Implementiere eine XOR-Verschlüsselung (<code>^</code>-Operator), sodass jedes Byte einer Original-Nachricht der Reihe nach mit dem entsprechenden Byte eines Schlüsselwortes verschlüsselt wird und gebe die verschlüsselte Nachricht als Text aus.</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Implementiere zur vorherigen Verschlüsselung eine Entschlüsselung.</p> |
| | | </section> |
| | | <section> |
| | | <p>Lösung</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const word = Buffer.from("abcdef"); |
| | | const key = Buffer.from("xyz"); |
| | | const encrypted = Buffer.alloc(word.length, 0); |
| | | |
| | | // encrypt |
| | | for (let i = 0; i < word.length; i++) { |
| | | encrypted[i] = word[i] ^ key[i % key.length]; |
| | | } |
| | | |
| | | // decrypt |
| | | for (let i = 0; i < word.length; i++) { |
| | | word[i] = encrypted[i] ^ key[i % key.length]; |
| | | } |
| | | |
| | | console.log(word.toString()); // => "abcdef" |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Streams</h3> |
| | | <p>Konstanter Fluss von (meist) großen Datenmengen in kleinen Paketen</p> |
| | | </section> |
| | | <section> |
| | | <p>Gängige Typen von Streams</p> |
| | | <ul> |
| | | <li>Standard In / Out</li> |
| | | <li>HTTP-Requests / -Responses</li> |
| | | <li>TCP-Sockets</li> |
| | | <li>Lese- und Schreib-Streams auf Dateien</li> |
| | | <li>Große Datenbank-Ergebnisse mittels Cursor</li> |
| | | </ul> |
| | | </section> |
| | | <section> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const readableStream = fs.createReadStream("./source.txt"); |
| | | const writableStream = fs.createWriteStream("./target.txt"); |
| | | |
| | | readableStream.on("data", (data /* Buffer */) => { |
| | | // process data here |
| | | writableStream.write(data) |
| | | }); |
| | | |
| | | readableStream.on("end", (data) => { |
| | | // stream has finished, e.g. EOF (end of file) |
| | | }); |
| | | |
| | | // alternatively pipe content |
| | | readableStream.pipe(writableStream); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Lese eine beliebige Datei als Readable Stream, verschlüssele den Inhalt mit der XOR-Methode und schreibe die verschlüsselten Bytes in eine Zieldatei</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Ändere das Programm, sodass auf Konsolenebene ein Pipe (|) ermöglicht wird:</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | echo "Hello World!" | node index.js |
| | | </code> |
| | | </pre> |
| | | <p>Nutze dafür die implizit vorhandenen Streams von Standard In / Out:</p> |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | process.stdin; // Readable Stream |
| | | process.stdout; // Writable Stream |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <h3>Kryptographie</h3> |
| | | </section> |
| | | <section> |
| | | Mithilfe des nativen <code>crypto</code>-Moduls ist es möglich, moderne sichere kryptografische Verfahren zu verwenden: |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const crypto = require("crypto"); |
| | | </code> |
| | | </pre> |
| | | Hierzu nutzt NodeJS selber die weit verbreitete OpenSSL-Bibliothek |
| | | </section> |
| | | <section> |
| | | Einen MD5-Hash erzeugen: |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const crypto = require("crypto"); |
| | | |
| | | const hash = crypto.createHash("md5"); |
| | | const md5 = hash.update("abcdefg") |
| | | .digest(); // Buffer (default) |
| | | // .digest("hex") // Hexadezimal |
| | | // .digest("utf8") // Bytes forciert als Chars |
| | | |
| | | console.log(md5); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Erzeuge jeweils den md5- und den sha256-Hash als Hex-Wert der beiden Texte in der Datei <code>text-examples.txt</code> und vergleiche jeweils ihren md5- und sha256-Hash miteinander</p> |
| | | </section> |
| | | <section> |
| | | <h3>Zippen</h3> |
| | | </section> |
| | | <section> |
| | | Mit dem nativen Modul <code>zlib</code> ist es möglich, Kompression in seinen Anwendungen zu nutzen. |
| | | <pre> |
| | | <code class="js" data-trim data-line-numbers> |
| | | const zlib = require("zlib"); |
| | | |
| | | // Komprimieren |
| | | zlib.deflate("abc" /* buffer | string */, (err, compressed) => {}); |
| | | |
| | | // Dekomprimieren |
| | | zlib.inflate(compressed /* buffer */, (err, decompressed) => {}); |
| | | |
| | | // Als Stream |
| | | const zipper = zlib.createDeflate(); |
| | | const unzipper = zlib.createInflate(); |
| | | |
| | | source.pipe(zipper).pipe(target); |
| | | source.pipe(unzipper).pipe(target); |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Nutze die einfachen Varianten <code>zlib.deflate</code> und <code>zlib.inflate</code>, um einen beliebigen Text zu komprimieren und anschließend wieder zu dekomprimieren</p> |
| | | </section> |
| | | <section> |
| | | <p>Aufgabe</p> |
| | | <p>Nutze die Stream-Varianten <code>zlib.createDeflate</code> und <code>zlib.createInflate</code>, um jeweils ein getrenntes Programm für die Kompression und Dekompression mit <code>process.stdin</code> und <code>process.stdout</code> zu machen, sodass in der Konsole folgende Nutzung möglich ist:</p> |
| | | <pre> |
| | | <code class="bash" data-trim data-line-numbers> |
| | | echo "abc" | node compress.js | node decompress.js |
| | | cat file.txt | node compress.js > file.zipped |
| | | cat file.zipped | node decompress.js > file.txt |
| | | </code> |
| | | </pre> |
| | | </section> |
| | | </section> |
| | | </div> |