Cipta aplikasi sukan masa nyata menggunakan Node.js

PHPz
Lepaskan: 2023-09-03 12:41:09
asal
847 orang telah melayarinya

Dalam artikel hari ini, saya akan menunjukkan cara membuat aplikasi web yang memaparkan skor permainan langsung NHL. Markah akan dikemas kini secara automatik semasa permainan berlangsung.

Ini adalah artikel yang sangat menarik untuk saya kerana ia memberi saya peluang untuk menggabungkan dua hobi kegemaran saya: pembangunan dan sukan.

Teknologi yang akan digunakan untuk membuat aplikasi ialah:

  1. Node.js
  2. Socket.io
  3. MySportsFeeds.com
  4. Preact (seperti React)
  5. HTM

Jika anda belum memasang Node.js lagi, lawati halaman muat turunnya sekarang dan sediakannya sebelum meneruskan.

Apakah itu Socket.io?

Socket.io ialah teknologi yang menggunakan WebSocket untuk menyambungkan pelanggan ke pelayan. Dalam contoh ini, klien ialah pelayar web dan pelayan ialah aplikasi Node.js. Pelayan boleh mempunyai berbilang pelanggan yang disambungkan kepadanya pada bila-bila masa.

Setelah sambungan diwujudkan, pelayan boleh menghantar mesej kepada semua pelanggan atau kepada pelanggan tunggal. Sebagai balasan, pelanggan boleh menghantar mesej kepada pelayan, membolehkan komunikasi masa nyata dua hala.

Sebelum Socket.io, aplikasi web biasanya menggunakan AJAX, dengan kedua-dua pelanggan dan pelayan mengundi satu sama lain untuk acara. Sebagai contoh, panggilan AJAX berlaku setiap 10 saat untuk melihat sama ada terdapat sebarang mesej yang perlu diproses.

Tinjauan untuk mesej menghasilkan banyak overhed pada pelanggan dan pelayan kerana ia sentiasa mencari mesej apabila tiada.

Menggunakan Socket.io, anda boleh menerima mesej serta-merta tanpa perlu mencarinya, sekali gus mengurangkan overhed.

Contoh Aplikasi Socket.io

Sebelum kami bekerja dengan data gerakan masa nyata, mari buat contoh aplikasi untuk menunjukkan cara Socket.io berfungsi.

Pertama, saya akan mencipta aplikasi Node.js baharu. Navigasi ke folder yang anda mahukan projek anda, buat folder baharu untuk apl itu, kemudian buat apl baharu:

cd ~/Documents/Nodejs
mkdir SocketExample
cd SocketExample
npm init
Salin selepas log masuk

Saya menggunakan semua tetapan lalai.

Memandangkan kami sedang membuat aplikasi web, saya akan menggunakan pakej NPM yang dipanggil Express untuk memudahkan persediaan. Dalam gesaan arahan, pasang seperti berikut: npm installexpress --simpannpm installexpress --save

当然,我们需要安装 Socket.io 包: npm install socket.io --save

让我们从创建 Web 服务器开始。创建一个名为 index.js 的新文件,并将以下代码放入其中以使用 Express 创建 Web 服务器:

const app = require("express")();
const 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");
});
Salin selepas log masuk

如果您不熟悉 Express,上面的代码示例包含 Express 库并创建一个新的 HTTP 服务器。在此示例中,HTTP 服务器正在侦听端口 3000,例如https://localhost:3000。路由在站点的根目录“/”处创建。路由结果返回一个 HTML 文件:index.html。

在创建index.html文件之前,让我们通过设置Socket.io来完成服务器。将以下内容附加到您的 index.js 文件中以创建 Socket 服务器:

const io = require('socket.io')(http);

io.on('connection', function(socket){
    console.log('Client connection received');
});
Salin selepas log masuk

与 Express 类似,代码首先导入 Socket.io 库。它存储在名为 io 的变量中。接下来,使用 io 变量,通过 on 函数创建事件处理程序。正在监听的事件是连接。每次客户端连接到服务器时都会调用此事件。

现在让我们创建我们非常基本的客户端。创建一个名为 index.html 的新文件,并将以下代码放入其中:

<!DOCTYPE html>
<html>
    <head>
		<title>Socket.IO Example</title>
	</head>
	<body>
		<script src="/socket.io/socket.io.js"></script>
		<script type="module">
			const socket = io();
		</script>
	</body>
</html>
Salin selepas log masuk

上面的 HTML 加载 Socket.io 客户端 JavaScript 并初始化与服务器的连接。要查看示例,请启动 Node 应用程序: node index.js

然后,在浏览器中导航至 http://localhost:3000。页面上不会出现任何内容;但是,如果您查看运行 Node 应用程序的控制台,则会记录两条消息:

  1. HTTP 服务器在端口 3000 上启动
  2. 收到客户端连接

现在我们已经成功建立了套接字连接,让我们使用它吧。让我们首先从服务器向客户端发送消息。然后,当客户端收到消息时,它可以将响应发送回服务器。

让我们看一下缩写的index.js 文件:

io.on("connection", function (socket) {
    console.log("Client connection received");
	socket.emit("sendToClient", { hello: "world" });
	socket.on("receivedFromClient", function (data) {
		console.log(data);
	});
});
Salin selepas log masuk

之前的 io.on 函数已更新,包含几行新代码。第一个 socket.emit 将消息发送到客户端。 sendToClient 是事件的名称。通过命名事件,您可以发送不同类型的消息,以便客户端可以以不同的方式解释它们。第二个添加是 socket.on,其中还包含一个事件名称:receivedFromClient

Sudah tentu, kita perlu memasang pakej Socket.io: npm install socket.io --save🎜 🎜Mari mulakan dengan mencipta pelayan web. Buat fail baharu bernama index.js dan letakkan kod berikut di dalamnya untuk membuat pelayan web menggunakan Express: 🎜
const socket = io();

socket.on('sendToClient', function (data) {
    console.log(data);
    socket.emit('receivedFromClient', { my: 'data' });
});
Salin selepas log masuk
Salin selepas log masuk
🎜Jika anda baru menggunakan Express, contoh kod di atas termasuk perpustakaan Express dan mencipta pelayan HTTP baharu. Dalam contoh ini, pelayan HTTP sedang mendengar pada port 3000, seperti https://localhost:3000. Laluan dibuat pada direktori akar tapak "/". Hasil penghalaan mengembalikan fail HTML: index.html. 🎜 🎜Sebelum mencipta fail index.html, mari kita akhiri pelayan dengan menyediakan Socket.io. Tambahkan yang berikut pada fail index.js anda untuk membuat pelayan Soket: 🎜
HTTP server started on port 3000
Client connection received
{ my: 'data' }
Salin selepas log masuk
Salin selepas log masuk
🎜Sama seperti Express, kod tersebut mengimport perpustakaan Socket.io terlebih dahulu. Ia disimpan dalam pembolehubah bernama io. Seterusnya, gunakan pembolehubah io untuk membuat pengendali acara melalui fungsi on. Peristiwa yang diperdengarkan ialah sambungan. Acara ini dipanggil setiap kali pelanggan menyambung ke pelayan. 🎜 🎜Sekarang mari buat pelanggan kami yang sangat asas. Buat fail baharu bernama index.html dan letakkan kod berikut di dalamnya: 🎜
const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true);
msf.authenticate("********", "*********");

const 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
});
Salin selepas log masuk
Salin selepas log masuk
🎜HTML di atas memuatkan JavaScript klien Socket.io dan memulakan sambungan ke pelayan. Untuk melihat contoh, lancarkan aplikasi Node: node index.js🎜 🎜Kemudian, navigasi ke http://localhost:3000 dalam penyemak imbas anda. Tiada apa-apa akan muncul pada halaman; walau bagaimanapun, jika anda melihat konsol yang menjalankan aplikasi Node, dua mesej akan dilog: 🎜 🎜 🎜Pelayan HTTP bermula pada port 3000🎜 🎜Terima sambungan pelanggan🎜 🎜 🎜Sekarang kita telah berjaya mewujudkan sambungan soket, mari gunakannya. Mari kita hantar mesej dari pelayan kepada klien dahulu. Kemudian, apabila pelanggan menerima mesej, ia boleh menghantar respons kembali kepada pelayan. 🎜 🎜Mari kita lihat fail index.js yang disingkat: 🎜
const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true, null);
msf.authenticate("*******", "******");

const 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,
	});
};
Salin selepas log masuk
Salin selepas log masuk
🎜Fungsi io.on sebelumnya telah dikemas kini untuk memasukkan beberapa baris kod baharu. socket.emit pertama menghantar mesej kepada klien. sendToClient ialah nama acara. Dengan menamakan acara, anda boleh menghantar pelbagai jenis mesej supaya pelanggan boleh mentafsirkannya dengan cara yang berbeza. Penambahan kedua ialah socket.on, yang juga mengandungi nama acara: receivedFromClient. Ini akan mencipta fungsi yang menerima data daripada klien. Dalam kes ini, data akan dilog ke tetingkap konsol. 🎜

服务器端修改完成;它现在可以从任何连接的客户端发送和接收数据。

让我们通过更新客户端以接收 sendToClient 事件来完成此示例。当它接收到该事件时,它可以将 receivedFromClient 事件响应回服务器。

这是在 HTML 的 JavaScript 部分中完成的,因此在 index.html 文件中,我更新了 JavaScript,如下所示:

const socket = io();

socket.on('sendToClient', function (data) {
    console.log(data);
    socket.emit('receivedFromClient', { my: 'data' });
});
Salin selepas log masuk
Salin selepas log masuk

使用实例化的套接字变量,我们在服务器上具有与 socket.on 函数非常相似的逻辑。对于客户端,它正在监听 sendToClient 事件。一旦客户端连接,服务器就会发送此消息。当客户端收到它时,它会记录到浏览器中的控制台。然后,客户端使用与服务器发送原始事件相同的 socket.emit 。在本例中,客户端将 receivedFromClient 事件发送回服务器。当服务器收到消息时,会将其记录到控制台窗口。

亲自尝试一下。首先,在控制台中运行 Node 应用程序:node index.js。然后在浏览器中加载 http://localhost:3000。

检查 Web 浏览器控制台,您应该会看到记录以下 JSON 数据: {hello: “世界”}

然后,在运行 Node 应用程序的命令提示符中,您应该看到以下内容:

HTTP server started on port 3000
Client connection received
{ my: 'data' }
Salin selepas log masuk
Salin selepas log masuk

客户端和服务器都可以使用接收到的 JSON 数据来执行特定任务。一旦我们连接到实时体育数据,我们将了解更多信息。

运动数据

现在我们已经掌握了如何向客户端和服务器发送和接收数据,可以利用它来提供实时更新。我选择使用体育数据,尽管同样的理论并不限于体育。在开始这个项目之前,我研究了不同的运动数据。我选择的是 MySportsFeeds,因为他们提供免费的开发者帐户(我与他们没有任何关系)。为了访问实时数据,我注册了一个帐户,然后做了一笔小额捐款。捐款起价为 1 美元,数据每 10 分钟更新一次。这对于示例来说是有好处的。

您的帐户设置完毕后,您就可以继续设置对其 API 的访问权限。为了帮助实现这一点,我将使用他们的 NPM 包: npm install mysportsfeeds-node --save

安装包后,可以按如下方式进行 API 调用:

const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true);
msf.authenticate("********", "*********");

const 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
});
Salin selepas log masuk
Salin selepas log masuk

在上面的示例中,请务必将对验证函数的调用替换为您的用户名和密码。

以下代码执行 API 调用以获取今天的 NHL 记分牌。 fordate 变量是今天指定的。我还将 force 设置为 true ,以便始终返回响应,即使数据没有更改。

使用当前设置,API 调用的结果将写入文本文件。在最后一个例子中,这将被改变;但是,出于演示目的,可以在文本编辑器中查看结果文件以了解响应的内容。结果包含一个记分板对象。该对象包含一个名为 gameScore 的数组。该对象存储每场比赛的结果。每个对象都包含一个名为 game 的子对象。该对象提供有关谁正在玩的信息。

在游戏对象之外,还有一些变量提供游戏的当前状态。数据根据游戏状态而变化。例如,当游戏还没有开始时,只有几个变量告诉我们游戏没有进行并且还没有开始。

当游戏进行时,会提供有关得分、游戏进行的时间段以及剩余时间的附加数据。当我们进入下一节中显示游戏的 HTML 时,我们将看到这一点。

实时更新

我们已经掌握了拼图的所有碎片,所以现在是时候将拼图拼凑起来以揭示最终图片了。目前,MySportsFeeds 对向我们推送数据的支持有限,因此我们必须从他们那里轮询数据。幸运的是,我们知道数据每 10 分钟只更改一次,因此我们不需要通过过于频繁地轮询更改来增加开销。一旦我们轮询它们的数据,我们就可以将这些更新从服务器推送到所有连接的客户端。

为了执行轮询,我将使用 JavaScript setInterval 函数每 10 分钟调用一次 API(在我的例子中)以查找更新。收到数据后,会将一个事件发送到所有连接的客户端。当客户端收到事件时,游戏分数将在网络浏览器中使用 JavaScript 进行更新。

当 Node 应用程序首次启动时,MySportsFeeds 也会被调用。此数据将用于在第一个 10 分钟间隔之前连接的任何客户端。这存储在全局变量中。这个相同的全局变量作为间隔轮询的一部分进行更新。这将确保当任何新客户端在轮询后连接时,他们将拥有最新的数据。

为了帮助主 index.js 文件中的一些代码整洁,我创建了一个名为 data.js 的新文件。该文件将包含一个导出的函数(可在 index.js 文件中找到),该函数执行先前对 MySportsFeeds API 的调用。以下是该文件的完整内容:

const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true, null);
msf.authenticate("*******", "******");

const 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,
	});
};
Salin selepas log masuk
Salin selepas log masuk

导出 getData 函数并返回调用结果,在本例中是一个将在主应用程序中解析的 Promise。

现在让我们看看index.js文件的最终内容:

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const data = require("./data.js");

// Global variable to store the latest NHL results
let latestData;

// Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => {
    latestData = result;
});

app.get("/", function (req, res) {
	res.sendFile(__dirname + "/index.html");
});

http.listen(3000, function () {
	console.log("HTTP server started on port 3000");
});

io.on("connection", function (socket) {
	// when clients connect, send the latest data
	socket.emit("data", latestData);
});

// refresh data
setInterval(function () {
	data.getData().then((result) => {
		// Update latest results for when new client's connect
		latestData = result;

		// send it to all connected clients
		io.emit("data", result);

		console.log("Last updated: " + new Date());
	});
}, 300000);
Salin selepas log masuk

上面的前七行代码实例化了所需的库和全局 latestData 变量。最终使用的库列表是:Express、HTTP、Socket.io 以及刚刚创建的上述 data.js 文件。

完成必要的处理后,应用程序会为服务器首次启动时将连接的客户端填充 latestData

// Global variable to store the latest NHL results
const latestData;

// Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => { 
    latestData = result;
});
Salin selepas log masuk

接下来的几行设置了网站根页面(http://localhost:3000/)的路由,并启动HTTP服务器监听3000端口。

接下来,设置 Socket.io 来查找连接。当收到新连接时,服务器会发出一个名为 data 的事件,其中包含 latestData 变量的内容。

最后,最后一段代码创建轮询间隔。当间隔发生时,latestData 变量将使用 API 调用的结果进行更新。然后,该数据向所有客户端发出相同的数据事件。

// refresh data
setInterval(function() {
    data.getData().then((result) => { 
		// Update latest results for when new client's connect
		latestData = result; 
	
		// send it to all connected clients
		io.emit('data', result);
		
		console.log('Last updated: ' + new Date());
	});
}, 300000);
Salin selepas log masuk

您可能会注意到,当客户端连接并发出事件时,它会使用套接字变量发出事件。此方法将仅将事件发送到连接的客户端。在该间隔内,全局 io 用于发出事件。这会将事件发送给所有客户端。

服务器就完成了。让我们在客户端前端工作。在前面的示例中,我创建了一个基本的 index.html 文件,该文件设置客户端连接,该连接将记录来自服务器的事件并将事件发回。我将扩展该文件以包含已完成的示例。

因为服务器正在向我们发送一个 JSON 对象,所以我将使用 Preact,它就像 React 的优化版本(如果您不熟悉 React,那也没关系)。此外,我将使用 HTM。 HTM 将允许我使用像 React 的 JSX 这样的语法,而无需构建工具。此外,它还包括与 Preact 的集成。

首先,我需要创建一个 id 为 games 的 div

<div id="games"></div>
Salin selepas log masuk

然后,我将创建模板。以下是模板的完整脚本(您需要将其放入主要 HTML 脚本中):

import { html, render } from "https://esm.sh/htm/preact";
import { signal } from "https://esm.sh/@preact/signals";
const games = signal([]);
const socket = io();
socket.on("data", function (data) {
    games.value = data;
});

function ordinalSuffix(input) {
	const tenRemainder = input % 10,
		hundredRemainer = input % 100;
	if (tenRemainder == 1 && hundredRemainer != 11) {
		return input + "st";
	}
	if (tenRemainder == 2 && hundredRemainer != 12) {
		return input + "nd";
	}
	if (tenRemainder == 3 && hundredRemainer != 13) {
		return input + "rd";
	}
	return input + "th";
}
function timeLeft(time) {
	const minutes = Math.floor(time / 60);
	const seconds = time - minutes * 60;

	return minutes + ":" + ("0" + seconds).slice(-2);
}
function stats() {
	return html`${games.value.forEach(
		(game) =>
			html`<div class="game">
				<div>
					${game.game.awayTeam.City} ${game.game.awayTeam.Name} at at
					${game.game.homeTeam.City} ${game.game.homeTeam.Name}
				</div>
				<div>
					${(() => {
						if (game.isUnplayed) {
							return `Game Starts at ${game.game.time}`;
						} else if (game.isCompleted === "false") {
							return html`<div>
									Current Score: ${game.awayScore} - ${game.homeScore}
								</div>
								<div>
									${(() => {
										if (game.currentIntermission) {
											return `${ordinalPrefix(
												game.currentIntermission
											)} Intermission`;
										} else if (game.currentPeriod) {
											return html`${ordinalPrefix(
													game.currentPeriod
												)}<br />${timeLeft(
													game.currentPeriodSecondsRemaining
												)}`;
										} else {
											return `1st`;
										}
									})()}
								</div>`;
						} else {
							return `Final Score: ${game.awayScore} - ${game.homeScore}`;
						}
					})()}
				</div>
			</div>`
	)}`;
}
render(stats, document.getElementById("games"));
Salin selepas log masuk

这已经很多了!让我们一步步来看看。首先,我们导入 Preact、HTM 和称为 Preact Signals 的东西。我们稍后会详细讨论这一点。

接下来,我们建立 WebSocket 连接。此代码与我们之前的代码相同,除了事件名称和我们对数据执行的操作不同之外。您可能会注意到我们分配数据的对象是一个信号。这是在 Preact 中管理状态的快速方法。您可以在 Preact Signals 页面上阅读更多相关信息。

接下来,我们有一些辅助函数,稍后我们将在实际模板中使用它们。之后,我们就有了模板组件。首先,我们迭代所有游戏并返回每个游戏的标记。

标记的第一部分显示团队。然后,我们进入游戏数据的主要部分。

在下一节中,我们首先检查游戏是否已经开始。如果没有,我们将显示比赛何时开始。如果已经开始,我们会显示当前分数,以及当前时段和剩余时间。这是使用辅助函数的地方。

最后,如果比赛结束,我们只显示最终得分。脚本的最后一行只是在我们之前创建的 div 中渲染模板。

下面是混合了已完成的游戏、正在进行的游戏和尚未开始的游戏时的情况的示例。我不是一个很好的设计师,所以当开发人员制作自己的用户界面时,它看起来就像你所期望的那样。如果需要,您可以创建自己的 CSS 样式。

使用 Node.js 创建实时体育应用程序

这是 HTML 和 JavaScript 的结合体。



    
		Socket.IO Example
	
	
		
		
		<div id="games"></div>
	

Salin selepas log masuk

启动 Node 应用程序并浏览到 http://localhost:3000 亲自查看结果!

Setiap X minit, pelayan menghantar acara kepada pelanggan. Pelanggan akan melukis semula elemen permainan menggunakan data yang dikemas kini. Jadi, apabila anda memastikan tapak web dibuka dan menyemaknya dengan kerap, anda akan melihat data permainan dimuat semula semasa permainan sedang berjalan.

Kesimpulan

Produk akhir menggunakan Socket.io untuk mencipta pelayan yang disambungkan oleh pelanggan. Pelayan mendapat data dan menghantarnya kepada pelanggan. Apabila pelanggan menerima data, paparan boleh dikemas kini dengan lancar. Ini mengurangkan beban pada pelayan kerana klien hanya melakukan kerja apabila ia menerima acara daripada pelayan.

Soket tidak terhad kepada satu arah; pelanggan juga boleh menghantar mesej kepada pelayan. Apabila pelayan menerima mesej, ia boleh melakukan beberapa pemprosesan.

Apl sembang biasanya berfungsi dengan cara ini. Pelayan akan menerima mesej daripada klien dan kemudian menyiarkannya kepada semua pelanggan yang disambungkan untuk menunjukkan bahawa seseorang telah menghantar mesej baharu.

Semoga anda menikmati siaran ini kerana saya mencipta aplikasi sukan secara langsung ini untuk salah satu sukan kegemaran saya!

Siaran ini telah dikemas kini dengan sumbangan daripada Jacob Jackson. Jacob ialah pembangun web, penulis teknikal, pekerja bebas dan penyumbang sumber terbuka.

Atas ialah kandungan terperinci Cipta aplikasi sukan masa nyata menggunakan Node.js. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!