SignalR 是一个为 ASP.NET 开发者设计的库,它简化了在 Web 应用程序中添加实时功能的过程。实时功能指的是服务器能够在客户端没有发起请求的情况下主动向客户端推送内容的能力。这种技术使得服务器和客户端之间的通信更加动态和即时,非常适合需要实时更新数据的应用场景,如聊天应用、实时数据分析、多人协作编辑工具等。
SignalR 的特点:
**双向通信:**SignalR 允许服务器和客户端之间进行双向通信,即服务器可以主动向客户端发送数据,而不仅仅限于传统的客户端请求-服务器响应模式。**自动处理连接状态:**SignalR 自动处理客户端的连接状态,包括连接、断开连接以及重新连接等操作。
**多种传输方式:**SignalR 支持多种传输方式,如 WebSockets、Server-Sent Events (SSE)、Long Polling 等,以确保在不同的浏览器和网络环境下都能实现最佳的通信效果。
易于使用的 API: SignalR 提供了一个简单易用的 API,使得开发者可以轻松地在服务器端实现远程过程调用 (RPC),即在服务器端调用客户端的 JavaScript 函数,反之亦然。
**客户端支持:**除了 JavaScript 客户端之外,SignalR 还支持其他客户端平台,如 iOS、Android 和 .NET 客户端。
**分组和过滤:**SignalR 允许你将连接到同一个 Hub 的客户端分组,并对特定组发送消息。
使用场景:实时聊天应用:实现即时消息传送。
在线协作编辑:允许多个用户同时编辑同一个文档。
股票市场或体育赛事直播:实时更新数据到用户的界面。
在线游戏:实现玩家之间的实时互动。
SignalR 适用于 ASP.NET 和 ASP.NET Core 应用程序,并且是开源的,这意味着它可以免费使用,并且有一个活跃的社区支持。如果你正在使用 ASP.NET 或 ASP.NET Core 构建 Web 应用程序,并且需要实现实时功能,那么 SignalR 将是一个很好的选择。
下面使用ASP.NET Core应用程序跟做一个项目:使用VS
参考视频:011.SqlSugar介绍和安装_哔哩哔哩_bilibili
项目初始化

不要选错喽~

新建完成后的目录结构:

运行截图:

绘制聊天界面
修改Index.cshtml页面
html
@{
ViewData["Title"] = "Home Page";
}
<div class="container">
<p> 当前登录用户:@ViewBag.UserName [<a href="/Home/LogOut"> 退出</a>]</p>
<div id="content"></div>
<div contenteditable="true" id="messageInput"></div>
<div class="row">
<div class="col-6">
<input type="button" id="sendButton" value="Send Message"/>
<input type="button" id="findButton" value="Find Message" />
</div>
</div>
<div id="historyMessage"></div>
</div>
<style>
.container{
width:800px;
margin:0px auto;
}
#content{
width:100%;
height:300px;
/* 溢出隐藏 有滚轮 */
overflow-x:hidden;
border:1px solid #ccc;
}
#messageInput{
width:100%;
min-height:100px;
border:1px solid #bbb;
}
#historyMessage{
display:none;
width:100%;
height:300px;
overflow:hidden;
border:1px solid #ccc;
}
</style>
其中@ViewBag.UserName 需要在HomeController中设置一个默认值,如Rise,如图

运行结果:

新增登录页面
在HomeController中新增控制器
cs
public IActionResult Login()
{
return View();
}
右键Login新增视图或者在View爆红处Alt+Enter提示添加视图
会新增一个Login.cshtml文件
添加代码
html
<div id="login">
<h1 class="title"> 登录聊天室</h1>
<form action="/Home/Submit">
<table>
<tr>
<td>
用户名:
</td>
<td> <input placeholder="请输入用户名" name="name"/></td>
</tr>
<tr>
<td>密码:</td>
<td> <input placeholder="请输入密码" name="password" type="password"/></td>
</tr>
<tr>
<td colspan="2">
<div class="operation">
<button id="submit" type="submit"> 登录</button>
<button id="cancel" > 取消</button>
</div>
</td>
</tr>
</table>
</form>
</div>
<style>
table{
margin :200px auto;
border-collapse:separate;
border-spacing:10px;
}
.operation{
display:flex;
justify-content:space-evenly;
}
button{
width:80px;
}
.title{
justify-content: space-evenly;
}
</style>
/Home/Login 查看实现样式

介绍SignalR
SignalR是一个继承的客户端与服务器库 ,它基于浏览器的客户端和ASP.NET的服务器组件,允许进行双向多步对话。这种对话不受限制,可以进行单个无状态请求/响应数据交换,直到明确关闭。对话通过永久连接进行,使得客户端和服务器能够发送多个消息,并允许服务器做出相应答复。特别的是,SignalR还允许服务器向服务端发送异步消息,这与 Ajax技术相似,都是基于现有的技术实现的。 在实现客户端和服务端通信时,SignalR通常会使用JS的长轮询(long polling)。此外,随着WebSockets的出现,SignalR也支持WebSockets通信,从而提供了更高效的实时通信解决方案。
SignalR的应用场景非常广泛,例如实现实时聊天、服务器广播 等高频率实时功能 。通过SignalR,开发人员可以轻松地构建实时Web应用程序,实现服务器与客户端之间的实时数据同步,从而提高用户体验和应用程序的交互性 。此外,SignalR还提供了丰富的文档和示例,帮助开发人员快速入门和使用。无论是通过Visual Studio的不同版本进行开发,还是利用SignalR实现单页应用程序的实时更新,SignalR都为Web开发提供了强大的支持
官方介绍:SignalR 简介 | Microsoft Learn

添加SignalR客户端库
右键项目-点击添加-点击客户端库,如图

提供程序选择unpkg,输入库(不要输错,输入完成回车),点击选择特定文件就自动带出来了,勾选两个js,修改目标位置即可,如图,点击安装。

等待几秒看到解决方案管理器中的相对路径中是否存在两个js文件
存在即安装好了

创建SignalR中心
中心是一个类,用于处理客户端和服务器通信的高级管道,在SignalRWebApp项目文件夹中,创建Hubs文件夹。在Hubs文件夹中,使用以下代码创建ChatHub类.
项目右键,新建文件夹Hubs

新建的文件夹下,右键新建一个类ChartHub

ChatHub.cs中添加代码
cs
using Microsoft.AspNetCore.SignalR;
namespace SignalRWebWebApp.Hubs
{
/// <summary>
///
///创建SignalR中心
/// </summary>
public class ChatHub:Hub
{
/// <summary>
/// 发送消息
/// </summary>
/// <param name="user">用户名</param>
/// <param name="message">密码</param>
/// <returns></returns>
public async Task SendMessage(String user, String message)
{
await Clients.All.SendAsync("ReceiveMessage",user,message);
}
}
}
配置SignalR
必须将SignalR服务器配置为将SignalR请求传递给SignalR。
将一下突出显示的代码添加到Program.cs文件中
cs
builder.Services.AddSignalR();
app.MapHub<ChatHub>("/chatHub");
program.cs
cs
using SignalRWebWebApp.Hubs;
var builder = WebApplication.CreateBuilder(args);
//添加signalR服务
builder.Services.AddSignalR();
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
//配置
app.MapHub<ChatHub>("/chatHub");
app.Run();
添加SignalR客户端代码
wwwroot下的js文件夹,右键新建项

新增chat.js(javaScript文件)

在chat.js中添加打印代码
console.log("执行客户端代码");
在Views-Home-Index.cshtml中引入js文件
cs
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="js/signalr/dist/browser/signalr.min.js"></script>
<script src="js/chat.js"></script>
运行检查是否引入成功
F12查看,说明引入成功

修改chat.js代码
cs
"use strict";
//const { signalR } = require("./signalr/dist/browser/signalr");
//连接服务
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//禁止发送按钮,直到建立连接
$("#sendButton").hide();
//建立连接
connection.start().then(function () {
//连接成功,则显示发送按钮
$("#sendButton").show();
console.log("连接成功!");
}).catch(function (err) {
//连接失败则返回错误信息
return console.error(err.toString());
});
运行,存在SendMessage按钮说明连接成功

chat.js中添加代码,如下所示,已经可以互通了
cs
"use strict";
//const { signalR } = require("./signalr/dist/browser/signalr");
//连接服务
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//禁止发送按钮,直到建立连接
$("#sendButton").hide();
//建立连接
connection.start().then(function () {
//连接成功,则显示发送按钮
$("#sendButton").show();
console.log("连接成功!");
}).catch(function (err) {
//连接失败则返回错误信息
return console.error(err.toString());
});
//发送消息
$("#sendButton").click(function () {
var user = $("#userInput").val();
var message = $("#messageInput").text();
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString);
})
});
//接收消息
connection.on("ReceiveMessage", function (user, message, time) {
$("#content").append(`<p>${user} ${time}</p><p>${message}</p><br>`);
$("#content").animate({ scrollTop: 100000 });
})

sqlSugar介绍和安装
官网:SqlSugar .Net ORM 5.X 官网 、文档、教程 - SqlSugar 5x - .NET果糖网 (donet5.com)
ISqlSugarClient
在依赖项中右键管理NuGet程序包,搜索sqlSugarCore安装


依赖项中出现包说明安装成功

appsettings.json文件中添加连接数据库的字符串

在Program.cs中添加注入SqlSugar服务的
cs
builder.Services.AddTransient<ISqlSugarClient>(provider =>
{
SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = builder.Configuration.GetSection("conn").Value,
DbType = DbType.MySql,
IsAutoCloseConnection = true
});
return db;
});

UserService
新增service文件夹并新增两个文件IUserService和UserService

program.cs中注册服务
cs
//注册用户服务
builder.Services.AddTransient<IUserService, UserService>();
CodeFirst
添加

UserInfo和Usermessage
UserInfo代码
cs
using SqlSugar;
namespace SignalRWebWebApp.Models
{
public class UserInfo
{
[SugarColumn(IsPrimaryKey = true)]
public int Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
}
}
UserMessage代码
cs
using SqlSugar;
namespace SignalRWebWebApp.Models
{
public class Usermessage
{
[SugarColumn(IsPrimaryKey = true)]
public int Id { get; set; }
public string UserName { get; set; }
public string Content { get; set; }
public DateTime CreateDateTime { get; set; }
}
}
修改UserService.cs(注意两个命名空间名字)
cs
using System.Reflection;
using SqlSugar;
namespace SignalRWebWebApp.Service
{
public class UserService : IUserService
{
private readonly ISqlSugarClient _db;
public UserService(ISqlSugarClient db)
{
_db = db;
}
public bool CodeFirst()
{
try
{
//创建数据库
_db.DbMaintenance.CreateDatabase();
string nspace = "SignalRWebWebApp.Models";
Type[] ass = Assembly.LoadFrom(AppContext.BaseDirectory + "SignalRWebWebApp.dll").GetTypes()
.Where(p => p.Namespace == nspace).ToArray();
_db.CodeFirst.SetStringDefaultLength(200).InitTables(ass);
}
catch (Exception e)
{
return false;
}
return true;
}
}
}
修改IUserSercice.cs
cs
namespace SignalRWebWebApp.Service
{
public interface IUserService
{
bool CodeFirst();
}
}
修改HomeController
cs
using Microsoft.AspNetCore.Mvc;
using SignalRWebWebApp.Models;
using System.Diagnostics;
using SignalRWebWebApp.Service;
namespace SignalRWebWebApp.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IUserService _user;
public HomeController(ILogger<HomeController> logger,IUserService user)
{
_logger = logger;
_user = user;
}
public bool CodeFirst()
{
return _user.CodeFirst();
}
public IActionResult Index()
{
ViewBag.UserName = "Rise";
return View();
}
public IActionResult Login()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
}
}
运行访问/Home/CodeFirst 如果是true则数据库和表新建成功
如果是false则排查问题

获取注册用户信息接口
在UserService和IUserService中添加相关代码
IUserService
cs
using SignalRWebWebApp.Models;
namespace SignalRWebWebApp.Service
{
public interface IUserService
{
bool CodeFirst();
UserInfo GetUser(string name, string password);
}
}
UserService
cs
public UserInfo GetUser(string name, string password)
{
UserInfo userInfo = _db.Queryable<UserInfo>().First(n=>n.Name==name);
if (userInfo == null)
{
UserInfo newInfo = new UserInfo()
{
Id = Convert.ToInt32(Guid.NewGuid().ToString()),
Name = name,
Password = password
};
return _db.Insertable(newInfo).ExecuteReturnEntity();
}
else if (userInfo.Password == password)
{
return new UserInfo();
}
return userInfo;
}
获取消息记录接口
UserService、IUserService和HomeController中新增内容
IUserService
cs
List<UserMessage> GetMessages(int pageIndex,int pageSize);
UserService
cs
public List<UserMessage> GetMessages(int pageIndex, int pageSize)
{
return _db.Queryable<UserMessage>().ToOffsetPage(pageIndex, pageSize);
}
HomeController
cs
public JsonResult GetMessages(int pageIndex,int pageSize )
{
return Json(_user.GetMessages(pageIndex, pageSize));
}
首页重定向
program.cs添加
cs
//添加Session服务
builder.Services.AddSession();
//使用
app.UseSession();
修改HomeController
cs
public IActionResult Index()
{
var userName = HttpContext.Session.GetString("userName");
if (string.IsNullOrEmpty(userName))
{
return Redirect("/Home/Login");
}
ViewBag.UserName = userName;
return View();
}

登录逻辑
在HomeController中添加代码
cs
public IActionResult Submit(string name, string password)
{
UserInfo userInfo = _user.GetUser(name, password);
if (string.IsNullOrEmpty(userInfo.Id))
{
return Redirect("/Home/Error");
}
HttpContext.Session.SetString("userName",userInfo.Name);
Response.Cookies.Append("userName",userInfo.Name);
return Redirect("/");
}
退出逻辑
在HomeController中添加代码
cs
public IActionResult LogOut()
{
HttpContext.Session.Remove("userName");
Response.Cookies.Delete("userName");
return Redirect("/Home/Login");
}
补充SignalR中心逻辑
ChatHub中新增
cs
using Microsoft.AspNetCore.SignalR;
using SignalRWebWebApp.Models;
using SqlSugar;
namespace SignalRWebWebApp.Hubs
{
/// <summary>
///
///创建SignalR中心
/// </summary>
public class ChatHub:Hub
{
private ISqlSugarClient _db;
public ChatHub(ISqlSugarClient db)
{
_db = db;
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="user">用户名</param>
/// <param name="message">密码</param>
/// <returns></returns>
public async Task SendMessage(String user, String message)
{
DateTime time= DateTime.Now;
UserMessage userMessage = new UserMessage()
{
Id = Guid.NewGuid().ToString(),
UserName = user,
Content = message,
CreateDateTime = time,
};
_db.Insertable(userMessage).ExecuteCommand();
await Clients.All.SendAsync("ReceiveMessage",user,message,time.ToString());
}
}
}

查看历史聊天记录
在chat.js中新增
cs
//查看聊天记录
var pageIndex = 1;
$("#findMessage").click(function () {
$("#historyMessage").fadeIn();
$.post("/Home/GetMessages", { pageIndex: pageIndex, pageSize: 10 }, function (data) {
$.each(data, function (i, e) {
$("#historyMessage").append(`<p>${e.userName} ${e.createDateTime}</p><p>${e.content}</p><br>`);
});
})
})
