Node.js hat in seiner Standardbibliothek ein unterbewertetes Modul, das überraschend nützlich ist. Das Readline-Modul macht, was es auf der Box sagt: Es liest eine Eingabezeile vom Terminal. Dies kann verwendet werden, um dem Benutzer eine oder zwei Fragen zu stellen oder eine Eingabeaufforderung am unteren Bildschirmrand zu erstellen. In diesem Lernprogramm möchte ich die Leistungsfähigkeit von Readline demonstrieren und einen Echtzeit-CLI-Chatroom erstellen, der von Socket.io unterstützt wird. Der Client sendet nicht nur einfache Nachrichten, sondern verfügt auch über Befehle für Emotes /mir
, private Nachrichten mit / msg
, und erlauben, dass Spitznamen mit geändert werden / nick
.
Dies ist wahrscheinlich die einfachste Verwendung von Readline:
var readline = required ('readline'); var rl = readline.createInterface (process.stdin, process.stdout); rl.question ("Wie heißt du?") function (answer) console.log ("Hallo" + Antwort); rl.close (););
Wir schließen das Modul ein, erstellen die Readline-Schnittstelle mit den Standard-Eingabe- und -Ausgabeströmen und stellen dem Benutzer eine einmalige Frage. Dies ist die erste Verwendung von Readline: Fragen stellen. Wenn Sie etwas mit einem Benutzer bestätigen müssen, möglicherweise in der Form des beliebten "Wollen Sie das tun? (J / n)", das CLI-Tools durchdringt, readline.question ()
ist der Weg, es zu tun.
Die andere Funktionalität, die Readline bietet, ist die Eingabeaufforderung, die von ihrem Standard aus angepasst werden kann. ">"Zeichen und vorübergehend angehalten, um Eingaben zu verhindern. Für unseren Readline-Chat-Client wird dies unsere primäre Schnittstelle sein. Es wird ein einziges Vorkommen von readline.question ()
den Benutzer nach einem Spitznamen fragen, aber alles andere wird sein readline.prompt ()
.
Beginnen wir mit dem langweiligen Teil: Abhängigkeiten. Dieses Projekt macht Gebrauch von socket.io
, das socket.io-client
Paket und ansi-color
. Ihre packages.json
Die Datei sollte ungefähr so aussehen:
"name": "ReadlineChatExample", "version": "1.0.0", "description": "CLI-Chat mit readline und socket.io", "author": "Matt Harzewski", "abhängigkeiten": "socket .io ":" latest "," socket.io-client ":" latest "," ansi-color ":" latest "," private ": true
Lauf npm installieren
und du solltest gut sein.
Für dieses Tutorial verwenden wir einen unglaublich einfachen Socket.io-Server. Einfacher geht es nicht:
var socketio = erfordern ('socket.io'); // Listen an Port 3636 var io = socketio.listen (3636); io.sockets.on ('Verbindung', Funktion (Socket) // Sendet die Nachricht eines Benutzers an alle anderen Personen im Raum Socket.on ('Senden', Funktion (Daten) io.sockets.emit ('Nachricht', Daten); ); );
Sie müssen lediglich eine eingehende Nachricht von einem Client übernehmen und an alle anderen weiterleiten. Der Server wäre wahrscheinlich robuster für eine größere Anwendung, aber für dieses einfache Beispiel sollte es ausreichend sein.
Dies sollte im Projektverzeichnis als gespeichert werden server.js
.
Bevor wir zum unterhaltsamen Teil kommen, müssen wir unsere Abhängigkeiten einbeziehen, einige Variablen definieren und die Readline-Schnittstelle und die Socket-Verbindung starten.
var readline = required ('readline'), socketio = requir ('socket.io-client'), util = requir ('util'), color = requir ("ansi-color"). set; var nick; var socket = socketio.connect ('localhost', port: 3636); var rl = readline.createInterface (process.stdin, process.stdout);
Der Code ist an dieser Stelle ziemlich selbsterklärend. Wir haben unsere Nickname-Variable, die Socket-Verbindung (über die socket.io-client
Paket) und unsere Readline-Schnittstelle.
Socket.io stellt über Port eine Verbindung zu localhost her 3636
In diesem Beispiel würde dies natürlich in die Domäne und den Port Ihres eigenen Servers geändert, wenn Sie eine Produktions-Chat-App erstellen. (Es hat nicht viel Sinn, mit sich selbst zu chatten!)
Nun zur ersten Verwendung von Readline! Wir möchten den Benutzer nach der Wahl des Spitznamens fragen, wodurch er im Chatroom identifiziert wird. Dafür verwenden wir Readline's Frage()
Methode.
// Setze den Benutzernamen rl.question ("Bitte gib einen Spitznamen ein:", function (name) nick = name; var msg = nick + "hat sich dem Chat angeschlossen"; socket.emit ('send', type: ' notice ', message: msg); rl.prompt (true););
Wir setzen die Nick-Variable von vorher auf den vom Benutzer erfassten Wert, senden eine Nachricht an den Server (die an die anderen Clients weitergeleitet wird), dass unser Benutzer dem Chat beigetreten ist, und wechseln dann die Readline-Schnittstelle wieder in den Aufforderungsmodus. Das wahr
Wert übergeben an Prompt()
stellt sicher, dass das Eingabeaufforderungszeichen richtig angezeigt wird. (Andernfalls kann sich der Cursor an die Nullposition in der Zeile bewegen und>"wird nicht angezeigt.)
Leider hat Readline ein frustrierendes Problem mit der Prompt()
Methode. Es spielt sich nicht gut mit console.log ()
, die den Text in der gleichen Zeile wie das Eingabeaufforderungszeichen ausgeben wird.>"Charaktere überall und andere Verrückte. Um dies zu beheben, werden wir sie nicht verwenden console.log
an einem beliebigen Ort in dieser Anwendung. Stattdessen sollte die Ausgabe an diese Funktion übergeben werden:
function console_out (msg) process.stdout.clearLine (); process.stdout.cursorTo (0); console.log (msg); rl.prompt (true);
Diese leicht Hacky Solution stellt sicher, dass die aktuelle Zeile in der Konsole leer ist und sich der Cursor in der Nullposition befindet, bevor die Ausgabe gedruckt wird. Dann wird explizit verlangt, dass die Eingabeaufforderung anschließend erneut ausgegeben wird.
Für den Rest dieses Tutorials werden Sie sehen console_out ()
anstatt console.log ()
.
Es gibt zwei Arten von Eingaben, die ein Benutzer eingeben kann: Chat und Befehle. Wir wissen, dass Befehlen ein Schrägstrich vorangestellt ist, so dass man leicht zwischen den beiden unterscheiden kann.
Readline hat mehrere Event-Handler, aber der wichtigste ist zweifellos Linie
. Jedes Mal, wenn im Eingabestrom ein Zeilenvorschubzeichen (von der Return- oder der Eingabetaste) erkannt wird, wird dieses Ereignis ausgelöst. Wir müssen uns also einhaken Linie
für unseren Eingabehandler.
rl.on ('line', Funktion (line) if (line [0] == "/" && line.length> 1) var cmd = line.match (/ [az] + \ b /) [0 ]; var arg = line.substr (cmd.length + 2, line.length); chat_command (cmd, arg); else // Chatnachricht senden socket.emit ('send', type: 'chat', message: line, nick: nick); rl.prompt (true););
Wenn das erste Zeichen der Eingabezeile ein Schrägstrich ist, wissen wir, dass es sich um einen Befehl handelt, für den mehr Verarbeitung erforderlich ist. Ansonsten senden wir einfach eine normale Chat-Nachricht und setzen die Eingabeaufforderung zurück. Beachten Sie den Unterschied zwischen den Daten, die hier über den Socket gesendet werden, und für die Join-Nachricht im vorherigen Schritt. Es verwendet eine andere Art
, Der empfangende Client weiß also, wie er die Nachricht formatiert, und wir übergeben die Nick
auch variabel.
Der Befehlsname (cmd
) und der folgende Text (arg
) werden mit etwas Regex und Teilstringmagie isoliert, dann geben wir sie an eine Funktion weiter, die den Befehl verarbeitet.
function chat_command (cmd, arg) switch (cmd) case 'nick': var notice = nick + "änderte ihren Namen in" + arg; nick = arg; socket.emit ('send', type: 'notice', message: notice); brechen; case 'msg': var to = arg.match (/ [a-z] + \ b /) [0]; var message = arg.substr (bis.Länge, Arg.Länge); socket.emit ('send', type: 'tell', message: message, an: an, von: nick); brechen; case 'me': var emote = nick + "" + arg; socket.emit ('send', type: 'emote', message: emote); brechen; default: console_out ("Dies ist kein gültiger Befehl.");
Wenn der Benutzer eingibt / Nick Gollum
, das Nick
Variable wird auf zurückgesetzt Gollum
, wo könnte es gewesen sein Smeagol
vorher und ein Hinweis wird an den Server gesendet.
Wenn der Benutzer eingibt / msg bilbo Wo ist das wertvolle?
, Der gleiche reguläre Ausdruck wird verwendet, um den Empfänger und die Nachricht zu trennen, dann ein Objekt mit dem Typ von sagen
wird an den Server geschoben. Diese wird etwas anders angezeigt als eine normale Nachricht und sollte für andere Benutzer nicht sichtbar sein. Zwar wird unser übermäßig einfacher Server die Nachricht blind an alle weiterleiten, aber der Client ignoriert Meldungen, die nicht an den richtigen Benutzernamen adressiert sind. Ein robusterer Server könnte diskreter sein.
Der Emote-Befehl wird in der Form von verwendet Ich esse ein zweites Frühstück
. Der Spitzname wird dem Emote auf eine Weise vorangestellt, die jedem bekannt sein sollte, der IRC oder ein Multiplayer-Rollenspiel gespielt hat. Dann wird er auf den Server übertragen.
Nun benötigt der Client eine Möglichkeit, Nachrichten zu empfangen. Alles, was wir tun müssen, ist das Socket.io-Client Botschaft
Ereignis und formatieren Sie die Daten entsprechend für die Ausgabe.
socket.on ('message', Funktion (data) var leader; if (data.type == 'chat' && data.nick! = nick) leader = color ("<"+data.nick+"> "," grün "); console_out (leader + data.message); else if (data.type ==" notice ") console_out (color (data.message, 'cyan')); else if (data. type == "tell" && data.to == nick) leader = color ("[" + data.from + "->" + data.to + "]", "red"); console_out (führer + data.message ; else if (data.type == "emote") console_out (color (data.message, "cyan")););
Nachrichten mit einem Typ von Plaudern
Das waren nicht Der Client, der unseren Benutzernamen verwendet, wird mit dem Benutzernamen und dem Chat-Text angezeigt. Der Benutzer kann bereits sehen, was er in die Readline eingetippt hat. Es hat also keinen Sinn, ihn erneut auszugeben. Hier verwende ich die ansi-color
Paket, um die Ausgabe ein wenig zu färben. Es ist nicht unbedingt notwendig, aber es macht den Chat leichter zu folgen.
Nachrichten mit einem Typ von beachten
oder emote
werden unverändert gedruckt, obwohl sie in Cyan gefärbt sind.
Wenn die Nachricht ein ist sagen
und der Kurzname entspricht dem aktuellen Namen dieses Clients. Die Ausgabe hat die Form von [Jemand-> Du] Hallo!
. Das ist natürlich nicht besonders privat. Wenn du sehen wolltest jedermanns Nachrichten, alles, was Sie tun müssten, ist die && data.to == Nickname
Teil. Im Idealfall sollte der Server wissen, an welchen Client die Nachricht gesendet werden soll, und sollte sie nicht an Clients senden, die sie nicht benötigen. Dies führt jedoch zu unnötiger Komplexität, die den Rahmen dieses Tutorials sprengt.
Mal sehen, ob alles funktioniert. Starten Sie zum Testen den Server, indem Sie ihn ausführen Knoten server.js
und dann ein paar neue Terminalfenster öffnen. In den neuen Fenstern ausführen Knoten client.js
und geben Sie einen Spitznamen ein. Sie sollten dann in der Lage sein, zwischen ihnen zu chatten, vorausgesetzt, alles läuft gut.
.