
In diesem Artikel werde ich zeigen, wie Sie mit Node.js eine Webanwendung erstellen, mit der Sie die Ergebnisse von NHL-Spielen in Echtzeit verfolgen können. Die Indikatoren werden entsprechend den Änderungen der Punktzahl während der Spiele aktualisiert.
Ich habe es wirklich genossen, diesen Artikel zu schreiben, weil die Arbeit daran zwei Dinge beinhaltete, die ich liebte: Softwareentwicklung und Sport.
Im Laufe der Arbeit werden wir folgende Werkzeuge verwenden:
- Node.js;
- Socket.io;
- MySportsFeed.com.
Wenn Sie Node.js nicht installiert haben, besuchen Sie die Download-Seite und installieren Sie es. Dann fahren wir fort.
Was ist socket.io?
Dies ist die Technologie, die den Client mit dem Server verbindet. Im aktuellen Beispiel ist der Client ein Webbrowser und der Server die Datei Node.js. Der Server kann gleichzeitig mit mehreren Clients arbeiten.
Sobald die Verbindung hergestellt ist, kann der Server Nachrichten an alle Clients oder nur an einen von ihnen senden. Dies kann wiederum Nachrichten an den Server senden und die Kommunikation in zwei Richtungen ermöglichen.
Vor Socket.io wurden Webanwendungen normalerweise unter AJAX ausgeführt. Seine Verwendung sah vor, dass der Client den Server abfragen und umgekehrt nach neuen Ereignissen suchen musste. Beispielsweise könnten solche Abfragen alle 10 Sekunden durchgeführt werden, um nach neuen Nachrichten zu suchen.
Dies stellte eine zusätzliche Belastung dar, da die Suche nach neuen Nachrichten auch dann durchgeführt wurde, wenn sie überhaupt nicht vorhanden waren.
Bei Verwendung von Socket.io werden Nachrichten in Echtzeit empfangen, ohne dass ständig nach ihrer Anwesenheit gesucht werden muss, wodurch die Last verringert wird.
Beispielanwendung für Socket.io
Bevor wir mit der Erfassung von Wettbewerbsdaten in Echtzeit beginnen, erstellen wir eine Beispielanwendung, um die Funktionsweise von Socket.io zu demonstrieren.
Zuerst werde ich eine Node.js.-Anwendung erstellen. Im Konsolenfenster müssen Sie in das Verzeichnis C: \ GitHub \ NodeJS wechseln, einen neuen Ordner für die Anwendung erstellen und darin eine neue Anwendung:
cd \GitHub\NodeJS mkdir SocketExample cd SocketExample npm init
Ich habe die Standardeinstellungen beibehalten, Sie können das gleiche tun.
Da wir eine Webanwendung erstellen, verwende ich ein NPM-Paket namens Express, um die Installation zu vereinfachen. Führen Sie an der Eingabeaufforderung die folgenden Befehle aus: npm install express --save.
Natürlich müssen wir auch das Socket.io-Paket installieren: npm install socket.io --save
Jetzt müssen Sie den Webserver starten. Erstellen Sie dazu eine neue Datei index.js und platzieren Sie den folgenden Codeabschnitt:
var app = require('express')(); var http = require('http').Server(app); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); http.listen(3000, function(){ console.log('HTTP server started on port 3000'); });
Wenn Ihnen Express nicht bekannt ist, enthält das obige Codebeispiel die Express-Bibliothek. Hier erstellen wir einen neuen HTTP-Server. In dem Beispiel überwacht der HTTP-Server Port 3000, d. H.
localhost : 3000. Der Pfad führt zur Wurzel „/“. Das Ergebnis wird als HTML-Datei index index.html zurückgegeben.
Bevor Sie diese Datei erstellen, beenden Sie den Serverstart, indem Sie Socket.io konfigurieren. Führen Sie die folgenden Befehle aus, um einen Socket-Server zu erstellen:
var io = require('socket.io')(http); io.on('connection', function(socket){ console.log('Client connection received'); });
Wie bei Express beginnt der Code mit dem Importieren der Socket.io-Bibliothek. Dies wird durch die Variable io angezeigt. Erstellen Sie als Nächstes mit dieser Variablen einen Ereignishandler mit der Funktion on. Dieses Ereignis wird jedes Mal ausgelöst, wenn ein Client eine Verbindung zum Server herstellt.
Nun erstellen wir einen einfachen Client. Erstellen Sie dazu die Datei index.html und fügen Sie den folgenden Code ein:
<!doctype html> <html> <head> <title>Socket.IO Example</title> </head> <body> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script> </body> </html>
Der obige HTML-Code lädt den Socket.io-JavaScript-Client und initialisiert die Verbindung zum Server. Führen Sie Node: node index.js aus, um ein Beispiel anzuzeigen.
Geben Sie als Nächstes im Browser
localhost ein : 3000. Die Seite bleibt leer, aber wenn Sie auf die Konsole schauen, während Node ausgeführt wird, werden zwei Meldungen angezeigt:
Der HTTP-Server wurde an Port 3000 gestartet
Client-Verbindung empfangenNachdem wir uns erfolgreich verbunden haben, können wir die Arbeit fortsetzen. Senden Sie beispielsweise eine Nachricht vom Server an den Client. Wenn der Client es empfängt, wird eine Antwortnachricht gesendet:
io.on('connection', function(socket){ console.log('Client connection received'); socket.emit('sendToClient', { hello: 'world' }); socket.on('receivedFromClient', function (data) { console.log(data); }); });
Die vorherige io.on-Funktion wurde aktualisiert und enthält mehrere neue Codezeilen. Der erste, socket.emit, sendet eine Nachricht an den Client. sendToClient ist der Name des Ereignisses. Durch das Benennen von Ereignissen erhalten Sie die Möglichkeit, verschiedene Arten von Nachrichten zu senden, sodass der Client sie unterschiedlich interpretieren kann. Ein weiteres Update ist socket.on, das ebenfalls einen Ereignisnamen hat: receiveFromClient. All dies schafft eine Funktion, die Daten vom Client empfängt. In diesem Fall werden sie auch im Konsolenfenster aufgezeichnet.
Die abgeschlossenen Schritte schließen die Vorbereitung des Servers ab. Jetzt kann es Daten von jedem verbundenen Client empfangen und senden.
Beenden wir dieses Beispiel, indem wir den Client aktualisieren, indem wir das sendToClient-Ereignis empfangen. Wenn ein Ereignis empfangen wird, wird eine Antwort von Client empfangen.
Damit ist der JavaScript-Teil von HTML abgeschlossen. In index.html hinzufügen:
var socket = io(); socket.on('sendToClient', function (data) { console.log(data); socket.emit('receivedFromClient', { my: 'data' }); });
Mit der integrierten Socket-Variablen erhalten wir auf dem Server eine ähnliche Logik mit der Funktion socket.on. Der Client wartet auf das sendToClient-Ereignis. Sobald der Client verbunden ist, sendet der Server diese Nachricht. Der Client, der es empfängt, zeichnet das Ereignis in der Browserkonsole auf. Danach verwendet der Client dasselbe socket.emit wie der Server, auf dem das ursprüngliche Ereignis gesendet wurde. In diesem Fall sendet der Client das empfangene FromClient-Ereignis an den Server. Wenn er eine Nachricht erhält, wird diese im Konsolenfenster protokolliert.
Probieren Sie es selbst aus. Starten Sie zunächst in der Konsole Ihre Knotenanwendung: node index.js. Laden Sie dann
localhost : 3000 in den Browser.
Überprüfen Sie Ihre Browserkonsole, und in den JSON-Protokollen wird Folgendes angezeigt: {Hallo: "Welt"}
Während die Knotenanwendung ausgeführt wird, wird Folgendes angezeigt:
Der HTTP-Server wurde an Port 3000 gestartet
Client-Verbindung empfangen
{my: 'data'}Sowohl der Client als auch der Server können JSON-Daten verwenden, um bestimmte Aufgaben auszuführen. Mal sehen, wie Sie in Echtzeit mit Wettbewerbsdaten arbeiten können.
Wettbewerbsinformationen
Nachdem wir die Prinzipien des Sendens und Empfangens von Daten durch Client und Server verstanden haben, sollten Sie sicherstellen, dass Aktualisierungen in Echtzeit durchgeführt werden. Ich habe die Wettkampfdaten verwendet, obwohl dies nicht nur mit Sportinformationen möglich ist. Aber da wir damit arbeiten, müssen wir die Quelle finden. Sie werden
MySportsFeeds dienen. Der Service wird bezahlt - ab 1 US-Dollar pro Monat.
Sobald Ihr Konto eingerichtet ist, können Sie mit der API beginnen. Sie können das NPM-Paket dafür verwenden: npm install mysportsfeeds-node --save.
Nach der Installation des Pakets verbinden wir die API:
var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true); msf.authenticate("********", "*********"); var today = new Date(); msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true });
Ersetzen Sie im obigen Beispiel meine Daten durch Ihre.
Der Code führt einen API-Aufruf durch, um die heutigen NHL-Wettbewerbsergebnisse zu erhalten. Die fordate-Variable definiert das Datum. Ich habe auch force und true verwendet, um Daten zurückzugewinnen, auch wenn die Ergebnisse gleich sind.
Mit dem aktuellen Setup werden die Ergebnisse des API-Aufrufs in eine Textdatei geschrieben. Im letzten Beispiel werden wir dies ändern. Zu Demonstrationszwecken kann die Ergebnisdatei in einem Texteditor angezeigt werden, um den Inhalt der Antwort zu verstehen. In unserem Ergebnis sehen wir das Ergebnis-Tabellenobjekt. Dieses Objekt enthält ein Array namens gameScore. Es speichert das Ergebnis jedes Spiels. Jedes Objekt enthält wiederum ein untergeordnetes Objekt, das als Spiel bezeichnet wird. Dieses Objekt gibt Auskunft darüber, wer spielt.
Außerhalb des Spielobjekts gibt es mehrere Variablen, die den aktuellen Status des Spiels anzeigen. Daten ändern sich abhängig von den Ergebnissen. Wenn das Spiel noch nicht begonnen hat, werden Variablen verwendet, die Informationen darüber liefern, wann dies geschehen wird. Wenn das Spiel begonnen hat, werden zusätzliche Daten zu den Ergebnissen bereitgestellt, einschließlich Informationen darüber, welcher Zeitraum jetzt ist und wie viel Zeit noch übrig ist. Um besser zu verstehen, worum es geht, fahren wir mit dem nächsten Abschnitt fort.
Echtzeit-Updates
Wir haben alle Teile des Puzzles, also lasst es uns zusammenbauen! Leider unterstützt MySportsFeeds die Ausgabe von Daten nur eingeschränkt, sodass Sie ständig Informationen anfordern müssen. Hier gibt es einen positiven Punkt: Wir wissen, dass sich die Daten nur einmal alle 10 Minuten ändern, sodass es nicht erforderlich ist, den Dienst zu oft abzufragen. Empfangene Daten können vom Server an alle verbundenen Clients gesendet werden.
Um die erforderlichen Daten zu erhalten, verwende ich die JavaScript-Funktion setInterval, mit der Sie alle 10 Minuten (in meinem Fall) auf die API zugreifen können, um nach Updates zu suchen. Wenn Daten eintreffen, wird das Ereignis an alle verbundenen Clients gesendet. Die Ergebnisse werden dann über JavaScript in einem Webbrowser aktualisiert.
MySportsFeeds wird auch aufgerufen, wenn die Knotenanwendung zuerst gestartet wird. Die Ergebnisse werden für alle Clients verwendet, die vor dem ersten 10-Minuten-Intervall eine Verbindung herstellen. Informationen dazu werden in einer globalen Variablen gespeichert. Es wird wiederum im Rahmen einer Intervallumfrage aktualisiert. Dies stellt sicher, dass jeder der Kunden relevante Ergebnisse erzielt.
Damit in der Hauptdatei index.js alles in Ordnung ist, habe ich eine neue Datei mit dem Namen data.js erstellt. Es enthält eine aus index.js exportierte Funktion, die die MySportsFeeds-API zuvor aufruft. Hier ist der vollständige Inhalt dieser Datei:
var MySportsFeeds = require("mysportsfeeds-node"); var msf = new MySportsFeeds("1.2", true, null); msf.authenticate("*******", "******"); var today = new Date(); exports.getData = function() { return msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { fordate: today.getFullYear() + ('0' + parseInt(today.getMonth() + 1)).slice(-2) + ('0' + today.getDate()).slice(-2), force: true }); };
Die Funktion getData wird exportiert und gibt die Ergebnisse des Aufrufs zurück. Schauen wir uns an, was wir in der Datei index.js haben.
var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); var data = require('./data.js');
Die ersten sieben Codezeilen oben initialisieren die erforderlichen Bibliotheken und rufen die globale Variable latestData auf. Die neueste Liste der verwendeten Bibliotheken lautet: Express, HTTP-Server, erstellt mit Express, Socket.io sowie die gerade erstellte Datei data.js.
Unter Berücksichtigung der Anforderungen füllt die Anwendung die neuesten Daten (latestData) für Clients aus, die beim ersten Start des Servers eine Verbindung herstellen:
In den nächsten Zeilen wird der Pfad für die Hauptseite der Site festgelegt (in unserem Fall
localhost : 3000 /) und der HTTP-Server angewiesen, Port 3000 abzuhören.
Socket.io ist dann so konfiguriert, dass nach neuen Verbindungen gesucht wird. Wenn sie erkannt werden, sendet der Server Ereignisdaten mit dem Inhalt der neuesten Variablen.
Und schließlich erstellt der letzte Code das erforderliche Abfrageintervall. Wenn es erkannt wird, wird die Variable "latestData" mit den Ergebnissen des API-Aufrufs aktualisiert. Diese Daten übergeben dann das gleiche Ereignis an alle Clients.
Wie wir sehen, wird ein Client, der eine Verbindung herstellt und ein Ereignis definiert, mit einer Socket-Variablen ausgegeben. Auf diese Weise können Sie ein Ereignis an einen bestimmten verbundenen Client senden. Innerhalb des Intervalls wird das globale io verwendet, um das Ereignis auszulösen. Es sendet Daten an alle Clients. Das Server-Setup ist abgeschlossen.
Wie wird es aussehen?
Lassen Sie uns nun am Frontend des Clients arbeiten. In einem frühen Beispiel habe ich die Basisdatei index.html erstellt, die eine Verbindung zum Client herstellt, um Serverereignisse aufzuzeichnen und zu senden. Jetzt werde ich die Funktionen der Datei erweitern.
Da der Server uns ein JSON-Objekt sendet, verwende ich jQuery und eine jQuery-Erweiterung namens JsRender. Dies ist eine Vorlagenbibliothek. Dadurch kann ich eine HTML-Vorlage erstellen, mit der der Inhalt jedes NHL-Spiels auf bequeme Weise angezeigt wird. Jetzt können Sie die Breite seiner Fähigkeiten sehen. Der Code enthält mehr als 40 Zeilen, daher werde ich ihn in mehrere kleinere Abschnitte unterteilen und am Ende den gesamten HTML-Inhalt anzeigen.
Folgendes wird zum Anzeigen von Spieldaten verwendet:
<script id="gameTemplate" type="text/x-jsrender"> <div class="game"> <div> {{:game.awayTeam.City}} {{:game.awayTeam.Name}} at {{:game.homeTeam.City}} {{:game.homeTeam.Name}} </div> <div> {{if isUnplayed == "true" }} Game starts at {{:game.time}} {{else isCompleted == "false"}} <div>Current Score: {{:awayScore}} - {{:homeScore}}</div> <div> {{if currentIntermission}} {{:~ordinal_suffix_of(currentIntermission)}} Intermission {{else currentPeriod}} {{:~ordinal_suffix_of(currentPeriod)}}<br/> {{:~time_left(currentPeriodSecondsRemaining)}} {{else}} 1st {{/if}} </div> {{else}} Final Score: {{:awayScore}} - {{:homeScore}} {{/if}} </div> </div> </script>
Die Vorlage wird mithilfe des Skript-Tags definiert. Es enthält eine Vorlagenkennung und einen speziellen Skripttyp namens text / x-jsrender. Die Vorlage definiert einen Div-Container für jedes Spiel, der eine Spielklasse zum Anwenden eines bestimmten Basisstils enthält. In diesem Div befindet sich der Anfang der Vorlage.
Das nächste Div zeigt das Gastteam und das Host-Team an. Dies wird implementiert, indem der Name der Stadt und des Teams mit dem Spielobjekt aus MySportsFeeds-Daten kombiniert werden.
Mit {{: game.awayTeam.City}} definiere ich ein Objekt, das beim Rendern der Vorlage durch einen physischen Wert ersetzt wird. Diese Syntax wird von der JsRender-Bibliothek definiert.
Wenn das Spiel nicht gespielt wird, erscheint eine Zeile, in der das Spiel mit {{: game.time}} beginnt.
Bis das Spiel beendet ist, wird die aktuelle Punktzahl angezeigt: {{: awayScore}} - {{: homeScore}}. Und schließlich ein kleiner Trick, der bestimmt, wie spät es jetzt ist, und um zu klären, ob es jetzt eine Pause gibt.
Wenn die Variable currentIntermission in den Ergebnissen erscheint, verwende ich die durch ordinal_suffix_of definierte Funktion I, die die Periodennummer in den folgenden Text konvertiert: 1. (2., 3. usw.) Pause.
Wenn es keine Unterbrechung gibt, suche ich nach dem Wert von currentPeriod. Ordinal_suffix_of wird auch hier verwendet, um zu zeigen, dass sich das Spiel in der 1. (2., 3. usw.) Periode befindet.
Zusätzlich wird eine andere Funktion, die ich als time_left definiert habe, verwendet, um die Anzahl der verbleibenden Sekunden bis zum Ende des Zeitraums zu konvertieren. Zum Beispiel: 10:12.
Der letzte Teil des Codes zeigt das Endergebnis an, wenn das Spiel beendet ist.
Hier ist ein Beispiel dafür, wie es aussieht, wenn es eine gemischte Liste abgeschlossener Spiele, noch nicht abgeschlossener Spiele und noch nicht gestarteter Spiele gibt (ich bin kein sehr guter Designer, daher sieht das Ergebnis so aus, wie es sein sollte, wenn der Entwickler eine benutzerdefinierte erstellt Do-it-yourself-Anwendungsoberfläche):

Als Nächstes wird ein JavaScript-Snippet erstellt, das einen Socket, Hilfsfunktionen ordinal_suffix_of und time_left sowie eine Variable erstellt, die auf die generierte jQuery-Vorlage verweist.
<script> var socket = io(); var tmpl = $.templates("#gameTemplate"); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; </script>
Das letzte Fragment ist der Code zum Empfangen des Socket-Ereignisses und zum Rendern der Vorlage:
socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); });
Ich habe ein Trennzeichen mit einer Daten-ID. Das Ergebnis des Template-Renderings (tmpl.render) schreibt HTML in diesen Container. Was wirklich cool ist - die JsRender-Bibliothek kann ein Array von Daten aufnehmen, in diesem Fall data.scoreboard.gameScore, das jedes Element im Array durchläuft und ein Spiel pro Element erstellt.
Hier ist die oben versprochene endgültige Version, in der HTML und JavaScript zusammengestellt werden:
<!doctype html> <html> <head> <title>Socket.IO Example</title> </head> <body> <div id="data"> </div> <script id="gameTemplate" type="text/x-jsrender"> <div class="game"> <div> {{:game.awayTeam.City}} {{:game.awayTeam.Name}} at {{:game.homeTeam.City}} {{:game.homeTeam.Name}} </div> <div> {{if isUnplayed == "true" }} Game starts at {{:game.time}} {{else isCompleted == "false"}} <div>Current Score: {{:awayScore}} - {{:homeScore}}</div> <div> {{if currentIntermission}} {{:~ordinal_suffix_of(currentIntermission)}} Intermission {{else currentPeriod}} {{:~ordinal_suffix_of(currentPeriod)}}<br/> {{:~time_left(currentPeriodSecondsRemaining)}} {{else}} 1st {{/if}} </div> {{else}} Final Score: {{:awayScore}} - {{:homeScore}} {{/if}} </div> </div> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); var helpers = { ordinal_suffix_of: function(i) { var j = i % 10, k = i % 100; if (j == 1 && k != 11) { return i + "st"; } if (j == 2 && k != 12) { return i + "nd"; } if (j == 3 && k != 13) { return i + "rd"; } return i + "th"; }, time_left: function(time) { var minutes = Math.floor(time / 60); var seconds = time - minutes * 60; return minutes + ':' + ('0' + seconds).slice(-2); } }; var tmpl = $.templates("#gameTemplate"); socket.on('data', function (data) { console.log(data); $('#data').html(tmpl.render(data.scoreboard.gameScore, helpers)); }); </script> <style> .game { border: 1px solid #000; float: left; margin: 1%; padding: 1em; width: 25%; } </style> </body> </html>
Jetzt ist es an der Zeit, die Node-Anwendung zu starten und
localhost : 3000 zu öffnen, um das Ergebnis zu sehen!
Alle X Minuten sendet der Server ein Ereignis an den Client. Der Client wiederum zeichnet die Spielelemente mit aktualisierten Daten neu. Wenn Sie die Website offen lassen, werden die Ergebnisse der Spiele daher ständig aktualisiert.
Fazit
Das Endprodukt verwendet Socket.io, um einen Server zu erstellen, mit dem Clients eine Verbindung herstellen. Der Server ruft die Daten ab und sendet sie an den Client. Wenn ein Kunde Daten erhält, kann er die Ergebnisse schrittweise aktualisieren. Dies reduziert die Belastung des Servers, da der Client nur dann handelt, wenn er ein Ereignis vom Server empfängt.
Der Server kann Nachrichten an den Client und den Client wiederum an den Server senden. Wenn der Server die Nachricht empfängt, führt er die Datenverarbeitung durch.
Chat-Anwendungen funktionieren ähnlich. Der Server empfängt eine Nachricht vom Client und sendet dann alle Daten an die verbundenen Clients, um anzuzeigen, dass jemand eine neue Nachricht gesendet hat.
Ich hoffe, Ihnen hat dieser Artikel gefallen, denn als ich diese Echtzeit-Sportanwendung entwickelte, hatte ich gerade einen Berg an Vergnügen, den ich mit meinem Wissen teilen wollte. Hockey ist schließlich eine meiner Lieblingssportarten!
