前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2022年12月 .NET CORE 即时通讯-使用SignalR进行井字游戏

【愚公系列】2022年12月 .NET CORE 即时通讯-使用SignalR进行井字游戏

作者头像
愚公搬代码
发布2022-12-10 14:25:57
4760
发布2022-12-10 14:25:57
举报
文章被收录于专栏:历史专栏历史专栏

文章目录


前言

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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 文件。

代码语言:javascript
复制
//添加SignalR服务
builder.Services.AddSignalR();
//配置路由
app.MapHub<GameHub>("/game");
在这里插入图片描述
在这里插入图片描述

4.添加 SignalR 客户端代码

使用以下代码替换 Pages/Index.cshtml 中的内容:

代码语言:javascript
复制
@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是井字游戏,规则是:在一个井字格子的棋盘里下棋,横竖斜一旦三子连子,则胜。而事实上,遵循一定的规则,该游戏便能保证不败,即至少是平局。

代码语言:javascript
复制
"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.运行应用

在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、SignalR的基本使用
    • 1.添加 SignalR 客户端库
      • 2.创建 SignalR 中心
        • 3.配置 SignalR
          • 4.添加 SignalR 客户端代码
            • 5.运行应用
            相关产品与服务
            内容分发网络 CDN
            内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档