【愚公系列】2022年12月 .NET CORE 即时通讯-使用SignalR进行井字游戏
【摘要】 前言ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。 一、SignalR的基本使用SignalR的基本使用步骤如下所示:创建 Web 项目。添加 SignalR 客户端库。创建 SignalR 中心。配置项目以使用 SignalR。添加可将消息从任何客户端发送到所有连接客户端的代...
前言
ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。
一、SignalR的基本使用
SignalR的基本使用步骤如下所示:
- 创建 Web 项目。
- 添加 SignalR 客户端库。
- 创建 SignalR 中心。
- 配置项目以使用 SignalR。
- 添加可将消息从任何客户端发送到所有连接客户端的代码。
本文以.NET 7为例,创建 Web 项目就不做多说明。
1.添加 SignalR 客户端库
ASP.NET Core 共享框架中包含 SignalR 服务器库。 JavaScript 客户端库不会自动包含在项目中。使用库管理器 (LibMan) 从 unpkg 获取客户端库。 unpkg 是一个快速的全局内容分发网络,适用于 npm 上的所有内容。
1、在“解决方案资源管理器”>中,右键单击项目,然后选择“添加”“客户端库”。
2、在“添加客户端库”对话框中
- 为“提供程序”选择“unpkg”
- 对于“库”,输入 @microsoft/signalr@latest
- 选择“选择特定文件”,展开“dist/browser”文件夹,然后选择 signalr.js 和 signalr.min.js。
- 将“目标位置”设置为 wwwroot/lib/microsoft/signalr/
- 选择“安装”
2.创建 SignalR 中心
中心是一个类,用作处理客户端 - 服务器通信的高级管道。
GameHub.cs
public class GameHub : Hub
{
private static int Id;
private static Game _game = new();
public Task Join(IHubContext<GameHub> hubContext)
{
var id = Interlocked.Increment(ref Id);
_game.AddPlayer(Context.ConnectionId, hubContext);
return Clients.Caller.SendAsync("PlayerRegistered", id);
}
public void Result(int square)
{
if (Context.ConnectionId == _game.ClientTurn)
{
_game.ClientResult.SetResult(square);
}
else
{
//...
}
}
}
Game.cs
public class Game
{
private string _id1;
private string _id2;
private int[] _squares = new int[9];
//添加玩家,2个人就开始游戏
public void AddPlayer(string id, IHubContext<GameHub> hubContext)
{
if (string.IsNullOrEmpty(_id1))
{
_id1 = id;
}
else
{
_id2 = id;
_ = RunGame(hubContext);
}
}
//开始玩游戏
public async Task RunGame(IHubContext<GameHub> hubContext)
{
//取消多线程
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
while (true)
{
var square = await hubContext.Clients.Client(_id1).InvokeAsync<int>("Turn", cts.Token);
_squares[square - 1] = 1;
await hubContext.Clients.Client(_id2).SendAsync("Disable", square);
if (await GameOver())
{
break;
}
ResetCts(ref cts);
square = await hubContext.Clients.Client(_id2).InvokeAsync<int>("Turn", cts.Token);
_squares[square - 1] = 2;
await hubContext.Clients.Client(_id1).SendAsync("Disable", square);
if (await GameOver())
{
break;
}
ResetCts(ref cts);
}
cts.Dispose();
#region GameOver check
async Task<bool> GameOver()
{
if (_squares[0] != 0 && _squares[0] == _squares[1] && _squares[1] == _squares[2])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[0]);
return true;
}
if (_squares[0] != 0 && _squares[0] == _squares[3] && _squares[3] == _squares[6])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[0]);
return true;
}
if (_squares[0] != 0 && _squares[0] == _squares[4] && _squares[4] == _squares[8])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[0]);
return true;
}
if (_squares[1] != 0 && _squares[1] == _squares[4] && _squares[4] == _squares[7])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[1]);
return true;
}
if (_squares[2] != 0 && _squares[2] == _squares[4] && _squares[4] == _squares[6])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[2]);
return true;
}
if (_squares[2] != 0 && _squares[2] == _squares[5] && _squares[5] == _squares[8])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[2]);
return true;
}
if (_squares[3] != 0 && _squares[3] == _squares[4] && _squares[4] == _squares[5])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[3]);
return true;
}
if (_squares[6] != 0 && _squares[6] == _squares[7] && _squares[7] == _squares[8])
{
await hubContext.Clients.Clients(_id1, _id2).SendAsync("Win", _squares[6]);
return true;
}
return false;
}
#endregion
}
private void ResetCts(ref CancellationTokenSource cts)
{
if (cts.TryReset())
{
cts.CancelAfter(TimeSpan.FromSeconds(30));
}
else
{
cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
}
}
public TaskCompletionSource<int>? ClientResult { get; private set; }
public string? ClientTurn { get; private set; }
}
3.配置 SignalR
必须将 SignalR 服务器配置为将 SignalR 请求传递给 SignalR。 将以下突出显示的代码添加到 Program.cs 文件。
//添加SignalR服务
builder.Services.AddSignalR();
//配置路由
app.MapHub<GameHub>("/game");
4.添加 SignalR 客户端代码
使用以下代码替换 Pages/Index.cshtml 中的内容:
@page
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/WebApplication17.styles.css" asp-append-version="true" />
<div id="main">
<input type="text" id="b1" readonly>
<input type="text" id="b2" readonly>
<input type="text" id="b3" readonly>
<br><br>
<input type="text" id="b4" readonly>
<input type="text" id="b5" readonly>
<input type="text" id="b6" readonly>
<br><br>
<input type="text" id="b7" readonly>
<input type="text" id="b8" readonly>
<input type="text" id="b9" readonly>
</div>
<p id="playerTurn"></p>
<br />
<p id="playerId"></p>
<input type="button" id="join" value="Join Game" />
<script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
@*井字游戏的规则是:在一个井字格子的棋盘里下棋,横竖斜一旦三子连子,则胜。而事实上,遵循一定的规则,该游戏便能保证不败,即至少是平局。*@
<script src="~/js/tictactoe.js"></script>
tictactoe.js是井字游戏,规则是:在一个井字格子的棋盘里下棋,横竖斜一旦三子连子,则胜。而事实上,遵循一定的规则,该游戏便能保证不败,即至少是平局。
"use strict";
let b1 = document.getElementById("b1");
let b2 = document.getElementById("b2");
let b3 = document.getElementById("b3");
let b4 = document.getElementById("b4");
let b5 = document.getElementById("b5");
let b6 = document.getElementById("b6");
let b7 = document.getElementById("b7");
let b8 = document.getElementById("b8");
let b9 = document.getElementById("b9");
let buttons = [b1, b2, b3, b4, b5, b6, b7, b8, b9];
disableButtons();
var connection = new signalR.HubConnectionBuilder().withUrl("/game").build();
let playerId = 0;
connection.on("PlayerRegistered", function (id) {
if (document.getElementById("join").disabled) {
if (playerId === 0) {
playerId = id;
document.getElementById("playerId").innerHTML = `You are ${id === 1 ? "X" : "O"}`;
}
if (id === 1) {
document.getElementById("playerTurn").innerHTML = "waiting for another player";
} else {
document.getElementById("playerTurn").innerHTML = "other players turn";
}
}
});
let promise;
connection.on("Win", function (winId) {
disableButtons();
if (winId === playerId) {
alert("you win");
} else {
alert("other player won");
}
});
connection.on("Disable", function (square) {
buttons[square - 1].disabled = true;
if (buttons[square - 1].value === '') {
buttons[square - 1].value = playerId === 1 ? 'O' : 'X';
}
});
connection.start().then(function () {
}).catch(function (err) {
return console.error(err.toString());
});
let promiseResolve;
function disableButtons() {
for (let i = 0; i < 9; i++) {
buttons[i].disabled = true;
buttons[i].removeEventListener("click", onClick);
}
}
connection.on("Turn", function () {
document.getElementById("playerTurn").innerHTML = "your turn";
enableButtons();
return promise;
});
function enableButtons() {
promise = new Promise(function (resolve, reject) {
promiseResolve = resolve;
});
for (let i = 0; i < 9; i++) {
if (buttons[i].disabled && buttons[i].value === '') {
buttons[i].disabled = false;
buttons[i].addEventListener("click", onClick);
}
}
}
function onClick(element) {
document.getElementById("playerTurn").innerHTML = "other players turn";
element.target.value = playerId === 1 ? 'X' : 'O';
disableButtons();
promiseResolve(Number(element.target.id.split('b')[1]));
}
document.getElementById("join").addEventListener("click", function (event) {
document.getElementById("join").disabled = true;
connection.send("Join");
});
5.运行应用
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)